kaze-cli 2.0.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.
- package/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/api/client.d.ts +15 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +129 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/universal-client.d.ts +36 -0
- package/dist/api/universal-client.d.ts.map +1 -0
- package/dist/api/universal-client.js +184 -0
- package/dist/api/universal-client.js.map +1 -0
- package/dist/cli/config-command.d.ts +29 -0
- package/dist/cli/config-command.d.ts.map +1 -0
- package/dist/cli/config-command.js +458 -0
- package/dist/cli/config-command.js.map +1 -0
- package/dist/cli/repl.d.ts +98 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +1782 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/config/interactive-setup.d.ts +26 -0
- package/dist/config/interactive-setup.d.ts.map +1 -0
- package/dist/config/interactive-setup.js +308 -0
- package/dist/config/interactive-setup.js.map +1 -0
- package/dist/config/manager.d.ts +125 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +240 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/setup.d.ts +29 -0
- package/dist/config/setup.d.ts.map +1 -0
- package/dist/config/setup.js +442 -0
- package/dist/config/setup.js.map +1 -0
- package/dist/context/memory.d.ts +65 -0
- package/dist/context/memory.d.ts.map +1 -0
- package/dist/context/memory.js +215 -0
- package/dist/context/memory.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +38 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +288 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/manager.d.ts +43 -0
- package/dist/mcp/manager.d.ts.map +1 -0
- package/dist/mcp/manager.js +230 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/types.d.ts +106 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +3 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/personality/manager.d.ts +24 -0
- package/dist/personality/manager.d.ts.map +1 -0
- package/dist/personality/manager.js +217 -0
- package/dist/personality/manager.js.map +1 -0
- package/dist/tools/codebase.d.ts +34 -0
- package/dist/tools/codebase.d.ts.map +1 -0
- package/dist/tools/codebase.js +291 -0
- package/dist/tools/codebase.js.map +1 -0
- package/dist/tools/filesystem.d.ts +41 -0
- package/dist/tools/filesystem.d.ts.map +1 -0
- package/dist/tools/filesystem.js +263 -0
- package/dist/tools/filesystem.js.map +1 -0
- package/dist/tools/terminal.d.ts +30 -0
- package/dist/tools/terminal.d.ts.map +1 -0
- package/dist/tools/terminal.js +217 -0
- package/dist/tools/terminal.js.map +1 -0
- package/dist/tools/todo.d.ts +58 -0
- package/dist/tools/todo.d.ts.map +1 -0
- package/dist/tools/todo.js +233 -0
- package/dist/tools/todo.js.map +1 -0
- package/dist/types/index.d.ts +104 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/personality.d.ts +21 -0
- package/dist/types/personality.d.ts.map +1 -0
- package/dist/types/personality.js +3 -0
- package/dist/types/personality.js.map +1 -0
- package/dist/ui/logger.d.ts +76 -0
- package/dist/ui/logger.d.ts.map +1 -0
- package/dist/ui/logger.js +488 -0
- package/dist/ui/logger.js.map +1 -0
- package/dist/ui/renderer.d.ts +52 -0
- package/dist/ui/renderer.d.ts.map +1 -0
- package/dist/ui/renderer.js +294 -0
- package/dist/ui/renderer.js.map +1 -0
- package/dist/ui/tui.d.ts +53 -0
- package/dist/ui/tui.d.ts.map +1 -0
- package/dist/ui/tui.js +131 -0
- package/dist/ui/tui.js.map +1 -0
- package/package.json +70 -0
package/dist/cli/repl.js
ADDED
|
@@ -0,0 +1,1782 @@
|
|
|
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.REPL = void 0;
|
|
40
|
+
const readline = __importStar(require("readline"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const fs = __importStar(require("fs/promises"));
|
|
43
|
+
const universal_client_1 = require("../api/universal-client");
|
|
44
|
+
const renderer_1 = require("../ui/renderer");
|
|
45
|
+
const logger_1 = require("../ui/logger");
|
|
46
|
+
const tui_1 = require("../ui/tui");
|
|
47
|
+
const filesystem_1 = require("../tools/filesystem");
|
|
48
|
+
const terminal_1 = require("../tools/terminal");
|
|
49
|
+
const codebase_1 = require("../tools/codebase");
|
|
50
|
+
const memory_1 = require("../context/memory");
|
|
51
|
+
const todo_1 = require("../tools/todo");
|
|
52
|
+
const config_command_1 = require("./config-command");
|
|
53
|
+
const setup_1 = require("../config/setup");
|
|
54
|
+
const interactive_setup_1 = require("../config/interactive-setup");
|
|
55
|
+
const manager_1 = require("../mcp/manager");
|
|
56
|
+
const manager_2 = require("../personality/manager");
|
|
57
|
+
class REPL {
|
|
58
|
+
client;
|
|
59
|
+
renderer;
|
|
60
|
+
logger;
|
|
61
|
+
tui;
|
|
62
|
+
fileTool;
|
|
63
|
+
terminalTool;
|
|
64
|
+
codeSearchTool;
|
|
65
|
+
memoryManager;
|
|
66
|
+
conversationManager;
|
|
67
|
+
todoManager;
|
|
68
|
+
mcpManager;
|
|
69
|
+
personalityManager;
|
|
70
|
+
rl;
|
|
71
|
+
configManager;
|
|
72
|
+
configCommand;
|
|
73
|
+
config;
|
|
74
|
+
running = false;
|
|
75
|
+
processes = new Map();
|
|
76
|
+
providerConfig;
|
|
77
|
+
mcpToolsCache = new Map();
|
|
78
|
+
tokensUsed = 0;
|
|
79
|
+
activePersonality;
|
|
80
|
+
constructor(config) {
|
|
81
|
+
this.configManager = config.configManager;
|
|
82
|
+
this.configCommand = new config_command_1.ConfigCommand(config.configManager, config.configDir);
|
|
83
|
+
this.config = {
|
|
84
|
+
apiKey: config.apiKey,
|
|
85
|
+
baseUrl: config.baseUrl,
|
|
86
|
+
model: config.model,
|
|
87
|
+
provider: config.provider,
|
|
88
|
+
configManager: config.configManager,
|
|
89
|
+
configDir: config.configDir,
|
|
90
|
+
workingDirectory: config.workingDirectory || process.cwd(),
|
|
91
|
+
maxHistory: config.maxHistory || 50,
|
|
92
|
+
enableTools: config.enableTools !== false,
|
|
93
|
+
maxTokens: config.maxTokens || 8192,
|
|
94
|
+
temperature: config.temperature || 1.0,
|
|
95
|
+
interfaceConfig: config.interfaceConfig || {},
|
|
96
|
+
experimentalFeatures: config.experimentalFeatures || { systemPromptToolCalling: false },
|
|
97
|
+
enableMCP: config.enableMCP !== false,
|
|
98
|
+
enablePersonalities: config.enablePersonalities !== false
|
|
99
|
+
};
|
|
100
|
+
this.providerConfig = {
|
|
101
|
+
name: config.provider,
|
|
102
|
+
baseUrl: config.baseUrl,
|
|
103
|
+
supportsTools: true,
|
|
104
|
+
supportsStreaming: true,
|
|
105
|
+
authType: 'bearer'
|
|
106
|
+
};
|
|
107
|
+
this.client = new universal_client_1.UniversalClient(this.config.apiKey, this.config.baseUrl, this.config.model, this.config.provider, this.providerConfig, this.config.experimentalFeatures);
|
|
108
|
+
this.renderer = new renderer_1.StreamRenderer();
|
|
109
|
+
this.logger = new logger_1.Logger({ level: 1 });
|
|
110
|
+
this.tui = new tui_1.TUI({ version: '2.0.0' });
|
|
111
|
+
this.fileTool = new filesystem_1.FileSystemTools();
|
|
112
|
+
this.terminalTool = new terminal_1.TerminalManager();
|
|
113
|
+
this.codeSearchTool = new codebase_1.CodebaseSearcher();
|
|
114
|
+
this.memoryManager = new memory_1.MemoryManager();
|
|
115
|
+
this.conversationManager = new memory_1.ConversationManager(this.config.workingDirectory);
|
|
116
|
+
this.todoManager = new todo_1.TodoManager();
|
|
117
|
+
if (config.enableMCP !== false) {
|
|
118
|
+
this.mcpManager = new manager_1.MCPManager({
|
|
119
|
+
configDir: config.configDir,
|
|
120
|
+
configManager: config.configManager,
|
|
121
|
+
logger: this.logger
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (config.enablePersonalities !== false) {
|
|
125
|
+
this.personalityManager = new manager_2.PersonalityManager(config.configDir);
|
|
126
|
+
}
|
|
127
|
+
this.rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async start() {
|
|
133
|
+
if (this.running)
|
|
134
|
+
return;
|
|
135
|
+
this.running = true;
|
|
136
|
+
this.tui.setInfo({
|
|
137
|
+
model: this.config.model,
|
|
138
|
+
provider: this.config.provider,
|
|
139
|
+
tokensUsed: this.tokensUsed
|
|
140
|
+
});
|
|
141
|
+
this.tui.activate();
|
|
142
|
+
this.tui.render();
|
|
143
|
+
this.logger.banner({
|
|
144
|
+
version: `v1.0.0`,
|
|
145
|
+
model: `${this.config.provider} • ${this.config.model}`,
|
|
146
|
+
mcpStatus: 'Connecting to MCP...'
|
|
147
|
+
});
|
|
148
|
+
if (this.personalityManager) {
|
|
149
|
+
this.personalityManager.initialize();
|
|
150
|
+
}
|
|
151
|
+
this.setupEventHandlers();
|
|
152
|
+
// Initialize MCP asynchronously after the CLI is running
|
|
153
|
+
setImmediate(async () => {
|
|
154
|
+
if (this.mcpManager) {
|
|
155
|
+
try {
|
|
156
|
+
await this.mcpManager.initialize();
|
|
157
|
+
await this.mcpManager.connectAllSilent();
|
|
158
|
+
await this.updateMCPToolsCache();
|
|
159
|
+
const status = await this.mcpManager.getServerStatus();
|
|
160
|
+
const mcpServers = status.filter((s) => s.connected).length;
|
|
161
|
+
const mcpStatus = `${mcpServers} MCP server${mcpServers !== 1 ? 's' : ''} connected`;
|
|
162
|
+
// Update TUI info line with MCP status
|
|
163
|
+
this.tui.setInfo({
|
|
164
|
+
model: this.config.model,
|
|
165
|
+
provider: this.config.provider,
|
|
166
|
+
tokensUsed: this.tokensUsed,
|
|
167
|
+
mcpStatus
|
|
168
|
+
});
|
|
169
|
+
this.tui.markDirty();
|
|
170
|
+
this.tui.render();
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
// If MCP initialization fails, show 0 connected
|
|
174
|
+
const mcpStatus = '0 MCP servers connected';
|
|
175
|
+
this.tui.setInfo({
|
|
176
|
+
model: this.config.model,
|
|
177
|
+
provider: this.config.provider,
|
|
178
|
+
tokensUsed: this.tokensUsed,
|
|
179
|
+
mcpStatus
|
|
180
|
+
});
|
|
181
|
+
this.tui.markDirty();
|
|
182
|
+
this.tui.render();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// No MCP manager, show 0 connected
|
|
187
|
+
const mcpStatus = '0 MCP servers connected';
|
|
188
|
+
this.tui.setInfo({
|
|
189
|
+
model: this.config.model,
|
|
190
|
+
provider: this.config.provider,
|
|
191
|
+
tokensUsed: this.tokensUsed,
|
|
192
|
+
mcpStatus
|
|
193
|
+
});
|
|
194
|
+
this.tui.markDirty();
|
|
195
|
+
this.tui.render();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
while (this.running) {
|
|
199
|
+
const input = await this.readMultiline();
|
|
200
|
+
if (!input.trim())
|
|
201
|
+
continue;
|
|
202
|
+
try {
|
|
203
|
+
await this.handleInput(input);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
this.logger.error(String(error));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
stop() {
|
|
211
|
+
this.running = false;
|
|
212
|
+
this.tui.deactivate();
|
|
213
|
+
this.rl.close();
|
|
214
|
+
this.processes.forEach((proc, id) => {
|
|
215
|
+
this.terminalTool.stopProcess(id);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async updateMCPToolsCache() {
|
|
219
|
+
if (!this.mcpManager)
|
|
220
|
+
return;
|
|
221
|
+
const servers = await this.mcpManager.listServers();
|
|
222
|
+
for (const server of servers) {
|
|
223
|
+
if (server.disabled)
|
|
224
|
+
continue;
|
|
225
|
+
try {
|
|
226
|
+
const toolsMap = await this.mcpManager.listTools(server.name);
|
|
227
|
+
const tools = toolsMap.get(server.name);
|
|
228
|
+
if (tools) {
|
|
229
|
+
this.mcpToolsCache.set(server.name, tools.map((tool) => ({
|
|
230
|
+
type: 'function',
|
|
231
|
+
function: {
|
|
232
|
+
name: `${server.name}_${tool.name}`,
|
|
233
|
+
description: tool.description,
|
|
234
|
+
parameters: tool.inputSchema
|
|
235
|
+
}
|
|
236
|
+
})));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// Silently skip this server - don't let one bad server break the whole MCP system
|
|
241
|
+
this.logger.warning(`Failed to load tools from MCP server ${server.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
setupEventHandlers() {
|
|
246
|
+
this.rl.on('SIGINT', () => {
|
|
247
|
+
if (this.processes.size > 0) {
|
|
248
|
+
this.logger.warning('Stopping running processes...');
|
|
249
|
+
this.processes.forEach((proc, id) => {
|
|
250
|
+
this.terminalTool.stopProcess(id);
|
|
251
|
+
});
|
|
252
|
+
this.processes.clear();
|
|
253
|
+
this.logger.success('All processes stopped.');
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log(chalk_1.default.hex(logger_1.THEME.dim)('\nUse "exit" or "quit" to leave Kaze CLI.'));
|
|
257
|
+
this.rl.prompt();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
this.rl.on('close', () => {
|
|
261
|
+
console.log(chalk_1.default.hex(logger_1.THEME.dim)('Goodbye!'));
|
|
262
|
+
process.exit(0);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async readLine() {
|
|
266
|
+
return new Promise((resolve) => {
|
|
267
|
+
this.rl.question(chalk_1.default.hex(logger_1.THEME.cyan)('> '), (answer) => {
|
|
268
|
+
resolve(answer);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async readMultiline() {
|
|
273
|
+
const lines = [];
|
|
274
|
+
let isFirstLine = true;
|
|
275
|
+
let shownHint = false;
|
|
276
|
+
while (true) {
|
|
277
|
+
if (isFirstLine) {
|
|
278
|
+
const line = await new Promise((resolve) => {
|
|
279
|
+
this.rl.question(chalk_1.default.hex(logger_1.THEME.cyan)('> '), (answer) => {
|
|
280
|
+
resolve(answer);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
if (line.trim() === '') {
|
|
284
|
+
return '';
|
|
285
|
+
}
|
|
286
|
+
lines.push(line);
|
|
287
|
+
isFirstLine = false;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
if (!shownHint) {
|
|
291
|
+
console.log(chalk_1.default.dim('... Press enter on newline to submit.'));
|
|
292
|
+
shownHint = true;
|
|
293
|
+
}
|
|
294
|
+
const line = await new Promise((resolve) => {
|
|
295
|
+
this.rl.question(chalk_1.default.dim('... '), (answer) => {
|
|
296
|
+
resolve(answer);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
if (line.trim() === '') {
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
lines.push(line);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return lines.join('\n');
|
|
306
|
+
}
|
|
307
|
+
async handleInput(input) {
|
|
308
|
+
const trimmedInput = input.trim();
|
|
309
|
+
if (trimmedInput.startsWith('/')) {
|
|
310
|
+
await this.handleCommand(trimmedInput);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (trimmedInput === 'exit' || trimmedInput === 'quit') {
|
|
314
|
+
this.stop();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (trimmedInput === 'help' || trimmedInput === '?') {
|
|
318
|
+
this.showHelp();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (trimmedInput === 'clear') {
|
|
322
|
+
this.logger.clear();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
await this.sendMessage(trimmedInput);
|
|
326
|
+
}
|
|
327
|
+
async handleCommand(command) {
|
|
328
|
+
const [cmd, ...args] = command.slice(1).split(' ');
|
|
329
|
+
switch (cmd) {
|
|
330
|
+
case 'config':
|
|
331
|
+
await this.configCommand.handle(args);
|
|
332
|
+
break;
|
|
333
|
+
case 'read':
|
|
334
|
+
await this.handleReadCommand(args.join(' '));
|
|
335
|
+
break;
|
|
336
|
+
case 'write':
|
|
337
|
+
await this.handleWriteCommand(args);
|
|
338
|
+
break;
|
|
339
|
+
case 'edit':
|
|
340
|
+
await this.handleEditCommand(args);
|
|
341
|
+
break;
|
|
342
|
+
case 'search':
|
|
343
|
+
await this.handleSearchCommand(args.join(' '));
|
|
344
|
+
break;
|
|
345
|
+
case 'run':
|
|
346
|
+
await this.handleRunCommand(args.join(' '));
|
|
347
|
+
break;
|
|
348
|
+
case 'stop':
|
|
349
|
+
await this.handleStopCommand(args[0]);
|
|
350
|
+
break;
|
|
351
|
+
case 'code':
|
|
352
|
+
await this.handleCodeSearchCommand(args.join(' '));
|
|
353
|
+
break;
|
|
354
|
+
case 'ls':
|
|
355
|
+
await this.handleListCommand(args[0] || '.');
|
|
356
|
+
break;
|
|
357
|
+
case 'todo':
|
|
358
|
+
await this.handleTodoCommand(args);
|
|
359
|
+
break;
|
|
360
|
+
case 'mem':
|
|
361
|
+
await this.handleMemoryCommand(args);
|
|
362
|
+
break;
|
|
363
|
+
case 'mcp':
|
|
364
|
+
await this.handleMcpCommand(args);
|
|
365
|
+
break;
|
|
366
|
+
case 'personality':
|
|
367
|
+
case 'persona':
|
|
368
|
+
await this.handlePersonalityCommand(args);
|
|
369
|
+
break;
|
|
370
|
+
case 'context':
|
|
371
|
+
this.showContext();
|
|
372
|
+
break;
|
|
373
|
+
case 'exit':
|
|
374
|
+
case 'quit':
|
|
375
|
+
this.stop();
|
|
376
|
+
break;
|
|
377
|
+
default:
|
|
378
|
+
console.log(chalk_1.default.red(`Unknown command: /${cmd}`));
|
|
379
|
+
console.log(chalk_1.default.gray('Type "help" for available commands.'));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async handleSetupCommand() {
|
|
383
|
+
console.log('');
|
|
384
|
+
this.logger.info('Starting interactive setup...');
|
|
385
|
+
const setup = new setup_1.ConfigSetup();
|
|
386
|
+
const configInfo = await setup.initialize();
|
|
387
|
+
const interactiveSetup = new interactive_setup_1.InteractiveSetup(configInfo.configDir, configInfo.envFile);
|
|
388
|
+
const success = await interactiveSetup.run();
|
|
389
|
+
if (success) {
|
|
390
|
+
await setup.markSetupComplete();
|
|
391
|
+
console.log('');
|
|
392
|
+
this.logger.success('Setup complete. You may need to restart for all changes to take effect.');
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
console.log('');
|
|
396
|
+
this.logger.error('Setup failed.');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async handleReadCommand(filePath) {
|
|
400
|
+
if (!filePath) {
|
|
401
|
+
console.log(chalk_1.default.red('Usage: /read <file_path>'));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const content = await this.fileTool.readFile(filePath);
|
|
406
|
+
console.log(chalk_1.default.cyan('Reading:'), filePath);
|
|
407
|
+
console.log(content);
|
|
408
|
+
this.conversationManager.addActiveFile(filePath);
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
this.logger.error('Error reading file: ' + String(error));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async handleWriteCommand(args) {
|
|
415
|
+
if (args.length < 2) {
|
|
416
|
+
console.log(chalk_1.default.red('Usage: /write <file_path> <content>'));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const filePath = args[0];
|
|
420
|
+
const content = args.slice(1).join(' ');
|
|
421
|
+
try {
|
|
422
|
+
await this.fileTool.writeFile(filePath, content);
|
|
423
|
+
this.logger.success(`Written to ${filePath}`);
|
|
424
|
+
this.conversationManager.addActiveFile(filePath);
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
this.logger.error('Error writing file: ' + String(error));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async handleEditCommand(args) {
|
|
431
|
+
if (args.length < 3) {
|
|
432
|
+
console.log(chalk_1.default.red('Usage: /edit <file_path> <search> <replace>'));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const filePath = args[0];
|
|
436
|
+
const search = args[1];
|
|
437
|
+
const replace = args[2];
|
|
438
|
+
try {
|
|
439
|
+
const fileResult = await this.fileTool.readFile(filePath);
|
|
440
|
+
const originalContent = fileResult.content;
|
|
441
|
+
this.logger.liveDiff(filePath, originalContent, search, replace);
|
|
442
|
+
const newContent = originalContent.replace(search, replace);
|
|
443
|
+
await this.fileTool.writeFile(filePath, newContent);
|
|
444
|
+
this.logger.success(`Successfully edited ${filePath}`);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
this.logger.error(`Error editing file: ${error}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async handleSearchCommand(query) {
|
|
451
|
+
if (!query) {
|
|
452
|
+
console.log(chalk_1.default.red('Usage: /search <query> [--path <path>] [--glob <pattern>]'));
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const results = await this.fileTool.searchFiles(query);
|
|
457
|
+
if (results.length === 0) {
|
|
458
|
+
this.logger.warning('No matches found.');
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
console.log(chalk_1.default.blue(`Found ${results.length} matches`));
|
|
462
|
+
results.forEach(result => {
|
|
463
|
+
console.log(chalk_1.default.white(result.path));
|
|
464
|
+
result.matches.forEach(match => {
|
|
465
|
+
console.log(chalk_1.default.gray(` Line ${match.line}:`), match.content.trim());
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
this.logger.error('Error searching: ' + String(error));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async handleRunCommand(command) {
|
|
474
|
+
if (!command) {
|
|
475
|
+
console.log(chalk_1.default.red('Usage: /run <command> [--async]'));
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
const isAsync = command.includes('--async');
|
|
480
|
+
const actualCommand = command.replace('--async', '').trim();
|
|
481
|
+
if (isAsync) {
|
|
482
|
+
const processId = await this.terminalTool.spawnLongRunningProcess(actualCommand, [], { cwd: this.config.workingDirectory });
|
|
483
|
+
this.processes.set(processId, actualCommand);
|
|
484
|
+
this.logger.success(`Started process: ${processId}`);
|
|
485
|
+
this.logger.info('Use /stop <id> to stop it.');
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
console.log(chalk_1.default.gray('$'), actualCommand);
|
|
489
|
+
const result = await this.terminalTool.executeCommand(actualCommand, [], { cwd: this.config.workingDirectory });
|
|
490
|
+
if (result.stdout) {
|
|
491
|
+
console.log(result.stdout);
|
|
492
|
+
}
|
|
493
|
+
if (result.stderr) {
|
|
494
|
+
console.log(chalk_1.default.red(result.stderr));
|
|
495
|
+
}
|
|
496
|
+
if (result.exitCode !== 0) {
|
|
497
|
+
this.logger.warning(`Exit code: ${result.exitCode}`);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
this.logger.success('Command completed successfully');
|
|
501
|
+
}
|
|
502
|
+
this.conversationManager.addCommandToHistory(actualCommand);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
this.logger.error('Error running command: ' + String(error));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async handleStopCommand(commandId) {
|
|
510
|
+
if (!commandId) {
|
|
511
|
+
if (this.processes.size === 0) {
|
|
512
|
+
console.log(chalk_1.default.yellow('No running processes.'));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
console.log(chalk_1.default.cyan('Running processes:'));
|
|
516
|
+
this.processes.forEach((_, id) => {
|
|
517
|
+
console.log(chalk_1.default.gray(` - ${id}`));
|
|
518
|
+
});
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const success = await this.terminalTool.stopProcess(commandId);
|
|
522
|
+
if (success) {
|
|
523
|
+
this.processes.delete(commandId);
|
|
524
|
+
console.log(this.renderer.renderSuccess(`Stopped process: ${commandId}`));
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
console.log(chalk_1.default.red(`Process not found: ${commandId}`));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async handleCodeSearchCommand(query) {
|
|
531
|
+
if (!query) {
|
|
532
|
+
console.log(chalk_1.default.red('Usage: /code <query> [--path <path>] [--max-results <n>]'));
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const results = await this.codeSearchTool.semanticSearch(query);
|
|
537
|
+
if (results.length === 0) {
|
|
538
|
+
console.log(chalk_1.default.yellow('No code matches found.'));
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
console.log(chalk_1.default.cyan(`Found ${results.length} code matches:\n`));
|
|
542
|
+
results.forEach(result => {
|
|
543
|
+
console.log(chalk_1.default.white(result.filePath));
|
|
544
|
+
console.log(chalk_1.default.gray(` Score: ${result.score.toFixed(2)}`));
|
|
545
|
+
console.log(chalk_1.default.gray(` Language: ${result.language}`));
|
|
546
|
+
console.log(chalk_1.default.gray(` Lines: ${result.startLine}-${result.endLine}`));
|
|
547
|
+
console.log(chalk_1.default.gray(` ${result.content.trim().substring(0, 100)}...`));
|
|
548
|
+
console.log();
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
console.error(chalk_1.default.red('Error searching code:'), error);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
async handleListCommand(path) {
|
|
556
|
+
try {
|
|
557
|
+
const entries = await this.fileTool.listDirectory(path);
|
|
558
|
+
if (entries.length === 0) {
|
|
559
|
+
console.log(chalk_1.default.yellow('Directory is empty.'));
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
console.log(chalk_1.default.cyan(`Contents of ${path}:\n`));
|
|
563
|
+
for (const entry of entries) {
|
|
564
|
+
const fullPath = path.replace(/\/$/, '') + '/' + entry;
|
|
565
|
+
const exists = await this.fileTool.fileExists(fullPath);
|
|
566
|
+
const isDir = exists ? (await fs.stat(fullPath)).isDirectory() : false;
|
|
567
|
+
const icon = isDir ? chalk_1.default.yellow('📁') : chalk_1.default.blue('📄');
|
|
568
|
+
console.log(`${icon} ${entry}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
console.error(chalk_1.default.red('Error listing directory:'), error);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
async handleTodoCommand(args) {
|
|
576
|
+
const subcommand = args[0];
|
|
577
|
+
switch (subcommand) {
|
|
578
|
+
case 'add':
|
|
579
|
+
this.handleTodoAdd(args.slice(1));
|
|
580
|
+
break;
|
|
581
|
+
case 'list':
|
|
582
|
+
this.handleTodoList();
|
|
583
|
+
break;
|
|
584
|
+
case 'done':
|
|
585
|
+
this.handleTodoDone(args[1]);
|
|
586
|
+
break;
|
|
587
|
+
case 'delete':
|
|
588
|
+
this.handleTodoDelete(args[1]);
|
|
589
|
+
break;
|
|
590
|
+
case 'summary':
|
|
591
|
+
this.showTodoSummary();
|
|
592
|
+
break;
|
|
593
|
+
default:
|
|
594
|
+
console.log(chalk_1.default.red('Usage: /todo <add|list|done|delete|summary> [args]'));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
handleTodoAdd(args) {
|
|
598
|
+
if (args.length === 0) {
|
|
599
|
+
this.logger.error('Usage: /todo add <task> [high|medium|low]');
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const task = args.slice(0, -1).join(' ');
|
|
603
|
+
const priority = args[args.length - 1] || 'medium';
|
|
604
|
+
const id = this.todoManager.addTodo(task, priority);
|
|
605
|
+
this.logger.success(`Added task: ${task} (${id})`);
|
|
606
|
+
}
|
|
607
|
+
handleTodoList() {
|
|
608
|
+
const todos = this.todoManager.getTodoTree();
|
|
609
|
+
if (todos.length === 0) {
|
|
610
|
+
this.logger.warning('No tasks.');
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
this.logger.section('Tasks');
|
|
614
|
+
const printTodo = (todo, indent = 0) => {
|
|
615
|
+
const prefix = ' '.repeat(indent);
|
|
616
|
+
const statusIcon = todo.status === 'completed' ? '✓' : todo.status === 'in_progress' ? '→' : '○';
|
|
617
|
+
const priorityColor = todo.priority === 'high' ? chalk_1.default.red : todo.priority === 'medium' ? chalk_1.default.yellow : chalk_1.default.gray;
|
|
618
|
+
const statusColor = todo.status === 'completed' ? chalk_1.default.green : chalk_1.default.white;
|
|
619
|
+
console.log(`${prefix}${statusColor(statusIcon)} ${priorityColor(`[${todo.priority}]`)} ${statusColor(todo.content)}`);
|
|
620
|
+
if (todo.subtasks && todo.subtasks.length > 0) {
|
|
621
|
+
todo.subtasks.forEach((subtask) => printTodo(subtask, indent + 1));
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
todos.forEach(todo => printTodo(todo));
|
|
625
|
+
}
|
|
626
|
+
handleTodoDone(id) {
|
|
627
|
+
if (!id) {
|
|
628
|
+
this.logger.error('Usage: /todo done <id>');
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const success = this.todoManager.setTodoStatus(id, 'completed');
|
|
632
|
+
if (success) {
|
|
633
|
+
this.logger.success(`Marked as done: ${id}`);
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
this.logger.error(`Task not found: ${id}`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
handleTodoDelete(id) {
|
|
640
|
+
if (!id) {
|
|
641
|
+
this.logger.error('Usage: /todo delete <id>');
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const success = this.todoManager.deleteTodo(id);
|
|
645
|
+
if (success) {
|
|
646
|
+
this.logger.success(`Deleted task: ${id}`);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
this.logger.error(`Task not found: ${id}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
showTodoSummary() {
|
|
653
|
+
const summary = this.todoManager.getSummary();
|
|
654
|
+
this.logger.section('Task Summary');
|
|
655
|
+
console.log(chalk_1.default.green(` ✓ Completed: ${summary.completed}`));
|
|
656
|
+
console.log(chalk_1.default.yellow(` → In Progress: ${summary.inProgress}`));
|
|
657
|
+
console.log(chalk_1.default.white(` ○ Pending: ${summary.pending}`));
|
|
658
|
+
}
|
|
659
|
+
async handleMemoryCommand(args) {
|
|
660
|
+
const subcommand = args[0];
|
|
661
|
+
switch (subcommand) {
|
|
662
|
+
case 'add':
|
|
663
|
+
this.handleMemoryAdd(args.slice(1));
|
|
664
|
+
break;
|
|
665
|
+
case 'list':
|
|
666
|
+
this.handleMemoryList(args[1]);
|
|
667
|
+
break;
|
|
668
|
+
case 'delete':
|
|
669
|
+
this.handleMemoryDelete(args[1]);
|
|
670
|
+
break;
|
|
671
|
+
default:
|
|
672
|
+
console.log(chalk_1.default.red('Usage: /mem <add|list|delete> [args]'));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
handleMemoryAdd(args) {
|
|
676
|
+
if (args.length < 3) {
|
|
677
|
+
console.log(chalk_1.default.red('Usage: /mem add <user|project> <category> <content>'));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const scope = args[0];
|
|
681
|
+
const category = args[1];
|
|
682
|
+
const content = args.slice(2).join(' ');
|
|
683
|
+
const id = this.memoryManager.addMemory({
|
|
684
|
+
content,
|
|
685
|
+
category: category,
|
|
686
|
+
scope: scope,
|
|
687
|
+
keywords: content.split(' ').slice(0, 5).join('|'),
|
|
688
|
+
title: content.substring(0, 50)
|
|
689
|
+
});
|
|
690
|
+
console.log(this.renderer.renderSuccess(`Added memory: ${id}`));
|
|
691
|
+
}
|
|
692
|
+
handleMemoryList(scope) {
|
|
693
|
+
const memories = scope === 'user'
|
|
694
|
+
? this.memoryManager.getUserMemories()
|
|
695
|
+
: scope === 'project'
|
|
696
|
+
? this.memoryManager.getProjectMemories()
|
|
697
|
+
: [...this.memoryManager.getUserMemories(), ...this.memoryManager.getProjectMemories()];
|
|
698
|
+
if (memories.length === 0) {
|
|
699
|
+
console.log(chalk_1.default.yellow('No memories.'));
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
console.log(chalk_1.default.cyan('Memories:\n'));
|
|
703
|
+
memories.forEach(mem => {
|
|
704
|
+
console.log(chalk_1.default.gray(` [${mem.scope}] [${mem.category}]`));
|
|
705
|
+
console.log(chalk_1.default.white(` ${mem.title}`));
|
|
706
|
+
console.log(chalk_1.default.gray(` Keywords: ${mem.keywords}`));
|
|
707
|
+
console.log();
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
handleMemoryDelete(id) {
|
|
711
|
+
if (!id) {
|
|
712
|
+
console.log(chalk_1.default.red('Usage: /mem delete <id>'));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const success = this.memoryManager.deleteMemory(id);
|
|
716
|
+
if (success) {
|
|
717
|
+
console.log(this.renderer.renderSuccess(`Deleted memory: ${id}`));
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
console.log(chalk_1.default.red(`Memory not found: ${id}`));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
async handleMcpCommand(args) {
|
|
724
|
+
if (!this.mcpManager) {
|
|
725
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const subcommand = args[0];
|
|
729
|
+
switch (subcommand) {
|
|
730
|
+
case 'list':
|
|
731
|
+
await this.handleMcpList();
|
|
732
|
+
break;
|
|
733
|
+
case 'add':
|
|
734
|
+
await this.handleMcpAdd(args.slice(1));
|
|
735
|
+
break;
|
|
736
|
+
case 'remove':
|
|
737
|
+
await this.handleMcpRemove(args[1]);
|
|
738
|
+
break;
|
|
739
|
+
case 'connect':
|
|
740
|
+
await this.handleMcpConnect(args[1]);
|
|
741
|
+
break;
|
|
742
|
+
case 'disconnect':
|
|
743
|
+
await this.handleMcpDisconnect(args[1]);
|
|
744
|
+
break;
|
|
745
|
+
case 'tools':
|
|
746
|
+
await this.handleMcpTools(args[1]);
|
|
747
|
+
break;
|
|
748
|
+
case 'enable':
|
|
749
|
+
await this.handleMcpEnable(args[1]);
|
|
750
|
+
break;
|
|
751
|
+
case 'disable':
|
|
752
|
+
await this.handleMcpDisable(args[1]);
|
|
753
|
+
break;
|
|
754
|
+
default:
|
|
755
|
+
console.log(chalk_1.default.red('Usage: /mcp <list|add|remove|connect|disconnect|tools|enable|disable> [args]'));
|
|
756
|
+
console.log(chalk_1.default.gray(' /mcp list - List all MCP servers'));
|
|
757
|
+
console.log(chalk_1.default.gray(' /mcp add <name> - Add a new MCP server (interactive)'));
|
|
758
|
+
console.log(chalk_1.default.gray(' /mcp remove <name> - Remove an MCP server'));
|
|
759
|
+
console.log(chalk_1.default.gray(' /mcp connect <name> - Connect to an MCP server'));
|
|
760
|
+
console.log(chalk_1.default.gray(' /mcp disconnect <name> - Disconnect from an MCP server'));
|
|
761
|
+
console.log(chalk_1.default.gray(' /mcp tools <name> - List tools from an MCP server'));
|
|
762
|
+
console.log(chalk_1.default.gray(' /mcp enable <name> - Enable an MCP server'));
|
|
763
|
+
console.log(chalk_1.default.gray(' /mcp disable <name> - Disable an MCP server'));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async handleMcpList() {
|
|
767
|
+
try {
|
|
768
|
+
if (!this.mcpManager) {
|
|
769
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const servers = await this.mcpManager.listServers();
|
|
773
|
+
if (servers.length === 0) {
|
|
774
|
+
console.log(chalk_1.default.yellow('No MCP servers configured.'));
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
console.log(chalk_1.default.cyan(`MCP Servers (${servers.length}):\n`));
|
|
778
|
+
for (const server of servers) {
|
|
779
|
+
if (!server.name)
|
|
780
|
+
continue;
|
|
781
|
+
const isConnected = await this.mcpManager.isConnected(server.name);
|
|
782
|
+
const status = (server.disabled === true)
|
|
783
|
+
? chalk_1.default.red('disabled')
|
|
784
|
+
: (isConnected ? chalk_1.default.green('connected') : chalk_1.default.yellow('disconnected'));
|
|
785
|
+
const transport = server.transport === 'stdio' ? 'stdio' : 'sse';
|
|
786
|
+
console.log(chalk_1.default.white(` ${server.name}`));
|
|
787
|
+
console.log(chalk_1.default.gray(` Status: ${status}`));
|
|
788
|
+
console.log(chalk_1.default.gray(` Transport: ${transport}`));
|
|
789
|
+
if (server.transport === 'stdio' && server.command) {
|
|
790
|
+
console.log(chalk_1.default.gray(` Command: ${server.command}`));
|
|
791
|
+
}
|
|
792
|
+
console.log();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
catch (error) {
|
|
796
|
+
console.error(chalk_1.default.red('Error listing MCP servers:'), error);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async handleMcpAdd(args) {
|
|
800
|
+
if (!this.mcpManager) {
|
|
801
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
if (args.length === 0) {
|
|
805
|
+
console.log(chalk_1.default.red('Usage: /mcp add <server_name>'));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const name = args[0];
|
|
809
|
+
console.log(chalk_1.default.cyan(`Adding MCP server: ${name}`));
|
|
810
|
+
console.log(chalk_1.default.gray('This will launch an interactive setup. Press Ctrl+C to cancel.\n'));
|
|
811
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
812
|
+
const rl = readline.createInterface({
|
|
813
|
+
input: process.stdin,
|
|
814
|
+
output: process.stdout
|
|
815
|
+
});
|
|
816
|
+
const question = (prompt) => {
|
|
817
|
+
return new Promise((resolve) => {
|
|
818
|
+
rl.question(prompt, (answer) => resolve(answer));
|
|
819
|
+
});
|
|
820
|
+
};
|
|
821
|
+
try {
|
|
822
|
+
const transport = await question('Transport (stdio/sse) [default: stdio]: ') || 'stdio';
|
|
823
|
+
if (transport !== 'stdio' && transport !== 'sse') {
|
|
824
|
+
console.log(chalk_1.default.red('Invalid transport. Must be stdio or sse.'));
|
|
825
|
+
rl.close();
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
const config = {
|
|
829
|
+
name,
|
|
830
|
+
transport
|
|
831
|
+
};
|
|
832
|
+
if (transport === 'stdio') {
|
|
833
|
+
const command = await question('Command (e.g., npx -y @modelcontextprotocol/server-filesystem): ');
|
|
834
|
+
if (!command) {
|
|
835
|
+
console.log(chalk_1.default.red('Command is required for stdio transport.'));
|
|
836
|
+
rl.close();
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
config.command = command;
|
|
840
|
+
const argsInput = await question('Arguments (space-separated, optional): ');
|
|
841
|
+
if (argsInput) {
|
|
842
|
+
config.args = argsInput.split(' ');
|
|
843
|
+
}
|
|
844
|
+
const envInput = await question('Environment variables (KEY=value, optional): ');
|
|
845
|
+
if (envInput) {
|
|
846
|
+
const env = {};
|
|
847
|
+
for (const pair of envInput.split(' ')) {
|
|
848
|
+
const [key, value] = pair.split('=');
|
|
849
|
+
if (key && value) {
|
|
850
|
+
env[key] = value;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
config.env = env;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
const url = await question('Server URL: ');
|
|
858
|
+
if (!url) {
|
|
859
|
+
console.log(chalk_1.default.red('URL is required for SSE transport.'));
|
|
860
|
+
rl.close();
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
config.url = url;
|
|
864
|
+
const headersInput = await question('Headers (JSON format, optional): ');
|
|
865
|
+
if (headersInput) {
|
|
866
|
+
try {
|
|
867
|
+
config.headers = JSON.parse(headersInput);
|
|
868
|
+
}
|
|
869
|
+
catch (error) {
|
|
870
|
+
console.log(chalk_1.default.red('Invalid JSON for headers.'));
|
|
871
|
+
rl.close();
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
await this.mcpManager.addServer(config);
|
|
877
|
+
console.log(this.renderer.renderSuccess(`Added MCP server: ${name}`));
|
|
878
|
+
await this.updateMCPToolsCache();
|
|
879
|
+
}
|
|
880
|
+
catch (error) {
|
|
881
|
+
console.error(chalk_1.default.red('Error adding MCP server:'), error);
|
|
882
|
+
}
|
|
883
|
+
finally {
|
|
884
|
+
rl.close();
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async handleMcpRemove(name) {
|
|
888
|
+
if (!this.mcpManager) {
|
|
889
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (!name) {
|
|
893
|
+
console.log(chalk_1.default.red('Usage: /mcp remove <server_name>'));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
const success = await this.mcpManager.removeServer(name);
|
|
898
|
+
if (success) {
|
|
899
|
+
console.log(this.renderer.renderSuccess(`Removed MCP server: ${name}`));
|
|
900
|
+
this.mcpToolsCache.delete(name);
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
console.log(chalk_1.default.red(`MCP server not found: ${name}`));
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
catch (error) {
|
|
907
|
+
console.error(chalk_1.default.red('Error removing MCP server:'), error);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async handleMcpConnect(name) {
|
|
911
|
+
if (!this.mcpManager) {
|
|
912
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (!name) {
|
|
916
|
+
console.log(chalk_1.default.red('Usage: /mcp connect <server_name>'));
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
await this.mcpManager.connectServer(name);
|
|
921
|
+
console.log(this.renderer.renderSuccess(`Connected to MCP server: ${name}`));
|
|
922
|
+
await this.updateMCPToolsCache();
|
|
923
|
+
}
|
|
924
|
+
catch (error) {
|
|
925
|
+
console.error(chalk_1.default.red('Error connecting to MCP server:'), error);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
async handleMcpDisconnect(name) {
|
|
929
|
+
if (!this.mcpManager) {
|
|
930
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (!name) {
|
|
934
|
+
console.log(chalk_1.default.red('Usage: /mcp disconnect <server_name>'));
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
try {
|
|
938
|
+
await this.mcpManager.disconnectServer(name);
|
|
939
|
+
console.log(this.renderer.renderSuccess(`Disconnected from MCP server: ${name}`));
|
|
940
|
+
this.mcpToolsCache.delete(name);
|
|
941
|
+
}
|
|
942
|
+
catch (error) {
|
|
943
|
+
console.error(chalk_1.default.red('Error disconnecting from MCP server:'), error);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
async handleMcpTools(name) {
|
|
947
|
+
if (!this.mcpManager) {
|
|
948
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (!name) {
|
|
952
|
+
console.log(chalk_1.default.red('Usage: /mcp tools <server_name>'));
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
try {
|
|
956
|
+
const toolsMap = await this.mcpManager.listTools(name);
|
|
957
|
+
const tools = toolsMap.get(name);
|
|
958
|
+
if (!tools || tools.length === 0) {
|
|
959
|
+
console.log(chalk_1.default.yellow(`No tools found for MCP server: ${name}`));
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
console.log(chalk_1.default.cyan(`Tools from ${name} (${tools.length}):\n`));
|
|
963
|
+
for (const tool of tools) {
|
|
964
|
+
console.log(chalk_1.default.white(` ${tool.name}`));
|
|
965
|
+
console.log(chalk_1.default.gray(` Description: ${tool.description || '(none)'}`));
|
|
966
|
+
console.log();
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
catch (error) {
|
|
970
|
+
console.error(chalk_1.default.red('Error listing tools:'), error);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
async handleMcpEnable(name) {
|
|
974
|
+
if (!this.mcpManager) {
|
|
975
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (!name) {
|
|
979
|
+
console.log(chalk_1.default.red('Usage: /mcp enable <server_name>'));
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
try {
|
|
983
|
+
await this.mcpManager.enableServer(name);
|
|
984
|
+
console.log(this.renderer.renderSuccess(`Enabled MCP server: ${name}`));
|
|
985
|
+
await this.updateMCPToolsCache();
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
console.error(chalk_1.default.red('Error enabling MCP server:'), error);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async handleMcpDisable(name) {
|
|
992
|
+
if (!this.mcpManager) {
|
|
993
|
+
console.log(chalk_1.default.red('MCP is not enabled. Set enableMCP: true in config.'));
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (!name) {
|
|
997
|
+
console.log(chalk_1.default.red('Usage: /mcp disable <server_name>'));
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
await this.mcpManager.disableServer(name);
|
|
1002
|
+
console.log(this.renderer.renderSuccess(`Disabled MCP server: ${name}`));
|
|
1003
|
+
this.mcpToolsCache.delete(name);
|
|
1004
|
+
}
|
|
1005
|
+
catch (error) {
|
|
1006
|
+
console.error(chalk_1.default.red('Error disabling MCP server:'), error);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
async handlePersonalityCommand(args) {
|
|
1010
|
+
if (!this.personalityManager) {
|
|
1011
|
+
console.log(chalk_1.default.red('Personalities are not enabled.'));
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const subcommand = args[0];
|
|
1015
|
+
switch (subcommand) {
|
|
1016
|
+
case 'list':
|
|
1017
|
+
this.handlePersonalityList();
|
|
1018
|
+
break;
|
|
1019
|
+
case 'use':
|
|
1020
|
+
await this.handlePersonalityUse(args[1]);
|
|
1021
|
+
break;
|
|
1022
|
+
case 'add':
|
|
1023
|
+
await this.handlePersonalityAdd(args.slice(1));
|
|
1024
|
+
break;
|
|
1025
|
+
case 'remove':
|
|
1026
|
+
await this.handlePersonalityRemove(args[1]);
|
|
1027
|
+
break;
|
|
1028
|
+
case 'import':
|
|
1029
|
+
await this.handlePersonalityImport(args);
|
|
1030
|
+
break;
|
|
1031
|
+
case 'export':
|
|
1032
|
+
await this.handlePersonalityExport(args);
|
|
1033
|
+
break;
|
|
1034
|
+
case 'search':
|
|
1035
|
+
this.handlePersonalitySearch(args[1]);
|
|
1036
|
+
break;
|
|
1037
|
+
case 'clear':
|
|
1038
|
+
this.handlePersonalityClear();
|
|
1039
|
+
break;
|
|
1040
|
+
default:
|
|
1041
|
+
console.log(chalk_1.default.red('Usage: /personality <list|use|add|remove|import|export|search|clear> [args]'));
|
|
1042
|
+
console.log(chalk_1.default.gray(' /personality list - List all personalities'));
|
|
1043
|
+
console.log(chalk_1.default.gray(' /personality use <name> - Use a personality'));
|
|
1044
|
+
console.log(chalk_1.default.gray(' /personality add <json> - Add a personality from JSON'));
|
|
1045
|
+
console.log(chalk_1.default.gray(' /personality remove <name> - Remove a personality'));
|
|
1046
|
+
console.log(chalk_1.default.gray(' /personality import <url> - Import a personality from URL'));
|
|
1047
|
+
console.log(chalk_1.default.gray(' /personality import <file> - Import a personality from file'));
|
|
1048
|
+
console.log(chalk_1.default.gray(' /personality export <name> <file> - Export a personality to file'));
|
|
1049
|
+
console.log(chalk_1.default.gray(' /personality search <query> - Search personalities'));
|
|
1050
|
+
console.log(chalk_1.default.gray(' /personality clear - Clear active personality'));
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
handlePersonalityList() {
|
|
1054
|
+
const personalities = this.personalityManager?.listPersonalities() || [];
|
|
1055
|
+
if (personalities.length === 0) {
|
|
1056
|
+
console.log(chalk_1.default.yellow('No personalities configured.'));
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
console.log(chalk_1.default.cyan(`Personalities (${personalities.length}):\\n`));
|
|
1060
|
+
for (const p of personalities) {
|
|
1061
|
+
const isActive = this.activePersonality?.name === p.name;
|
|
1062
|
+
const status = isActive ? chalk_1.default.green('[active]') : '';
|
|
1063
|
+
console.log(chalk_1.default.white(` ${p.name} ${status}`));
|
|
1064
|
+
console.log(chalk_1.default.gray(` Description: ${p.description}`));
|
|
1065
|
+
if (p.author) {
|
|
1066
|
+
console.log(chalk_1.default.gray(` Author: ${p.author}`));
|
|
1067
|
+
}
|
|
1068
|
+
if (p.tags && p.tags.length > 0) {
|
|
1069
|
+
console.log(chalk_1.default.gray(` Tags: ${p.tags.join(', ')}`));
|
|
1070
|
+
}
|
|
1071
|
+
console.log();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
async handlePersonalityUse(name) {
|
|
1075
|
+
if (!name) {
|
|
1076
|
+
console.log(chalk_1.default.red('Usage: /personality use <name>'));
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
if (!this.personalityManager)
|
|
1080
|
+
return;
|
|
1081
|
+
const personality = this.personalityManager.getPersonality(name);
|
|
1082
|
+
if (!personality) {
|
|
1083
|
+
console.log(chalk_1.default.red(`Personality "${name}" not found.`));
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
this.activePersonality = personality;
|
|
1087
|
+
console.log(this.renderer.renderSuccess(`Using personality: ${personality.name}`));
|
|
1088
|
+
console.log(chalk_1.default.gray(` ${personality.description}`));
|
|
1089
|
+
}
|
|
1090
|
+
async handlePersonalityAdd(args) {
|
|
1091
|
+
if (!this.personalityManager)
|
|
1092
|
+
return;
|
|
1093
|
+
const rl = readline.createInterface({
|
|
1094
|
+
input: process.stdin,
|
|
1095
|
+
output: process.stdout
|
|
1096
|
+
});
|
|
1097
|
+
const question = (prompt) => {
|
|
1098
|
+
return new Promise((resolve) => {
|
|
1099
|
+
rl.question(prompt, (answer) => resolve(answer));
|
|
1100
|
+
});
|
|
1101
|
+
};
|
|
1102
|
+
try {
|
|
1103
|
+
if (args.length > 0 && args[0].startsWith('{')) {
|
|
1104
|
+
const jsonStr = args.join(' ');
|
|
1105
|
+
const personality = JSON.parse(jsonStr);
|
|
1106
|
+
await this.personalityManager.addPersonality(personality);
|
|
1107
|
+
console.log(this.renderer.renderSuccess(`Added personality: ${personality.name}`));
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
console.log(chalk_1.default.cyan('Creating a new personality...\\n'));
|
|
1111
|
+
const name = await question('Name: ');
|
|
1112
|
+
if (!name) {
|
|
1113
|
+
console.log(chalk_1.default.red('Name is required.'));
|
|
1114
|
+
rl.close();
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const description = await question('Description: ');
|
|
1118
|
+
const author = await question('Author (optional): ');
|
|
1119
|
+
const tagsStr = await question('Tags (comma-separated, optional): ');
|
|
1120
|
+
const tags = tagsStr ? tagsStr.split(',').map(t => t.trim()) : [];
|
|
1121
|
+
console.log(chalk_1.default.gray('\\nEnter the system prompt (press Enter on empty line to finish):'));
|
|
1122
|
+
const systemPromptLines = [];
|
|
1123
|
+
while (true) {
|
|
1124
|
+
const line = await question('> ');
|
|
1125
|
+
if (line === '')
|
|
1126
|
+
break;
|
|
1127
|
+
systemPromptLines.push(line);
|
|
1128
|
+
}
|
|
1129
|
+
const systemPrompt = systemPromptLines.join('\\n');
|
|
1130
|
+
const temperatureStr = await question('Temperature (0-2, optional): ');
|
|
1131
|
+
const temperature = temperatureStr ? parseFloat(temperatureStr) : undefined;
|
|
1132
|
+
const maxTokensStr = await question('Max tokens (optional): ');
|
|
1133
|
+
const maxTokens = maxTokensStr ? parseInt(maxTokensStr, 10) : undefined;
|
|
1134
|
+
const personality = {
|
|
1135
|
+
name,
|
|
1136
|
+
description: description || 'No description',
|
|
1137
|
+
systemPrompt,
|
|
1138
|
+
temperature,
|
|
1139
|
+
maxTokens,
|
|
1140
|
+
version: '1.0.0',
|
|
1141
|
+
author: author || undefined,
|
|
1142
|
+
tags
|
|
1143
|
+
};
|
|
1144
|
+
await this.personalityManager.addPersonality(personality);
|
|
1145
|
+
console.log(this.renderer.renderSuccess(`Added personality: ${personality.name}`));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
catch (error) {
|
|
1149
|
+
console.error(chalk_1.default.red('Error adding personality:'), error);
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
rl.close();
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
async handlePersonalityRemove(name) {
|
|
1156
|
+
if (!name) {
|
|
1157
|
+
console.log(chalk_1.default.red('Usage: /personality remove <name>'));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
if (!this.personalityManager)
|
|
1161
|
+
return;
|
|
1162
|
+
const success = await this.personalityManager.removePersonality(name);
|
|
1163
|
+
if (success) {
|
|
1164
|
+
console.log(this.renderer.renderSuccess(`Removed personality: ${name}`));
|
|
1165
|
+
if (this.activePersonality?.name === name) {
|
|
1166
|
+
this.activePersonality = undefined;
|
|
1167
|
+
console.log(chalk_1.default.gray('Cleared active personality'));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
else {
|
|
1171
|
+
console.log(chalk_1.default.red(`Personality not found: ${name}`));
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
async handlePersonalityImport(args) {
|
|
1175
|
+
if (!this.personalityManager)
|
|
1176
|
+
return;
|
|
1177
|
+
if (args.length === 0) {
|
|
1178
|
+
console.log(chalk_1.default.red('Usage: /personality import <url|file>'));
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
const source = args[0];
|
|
1182
|
+
const overwrite = args.includes('--overwrite');
|
|
1183
|
+
try {
|
|
1184
|
+
let personality;
|
|
1185
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
1186
|
+
console.log(chalk_1.default.cyan(`Importing personality from URL: ${source}`));
|
|
1187
|
+
personality = await this.personalityManager.importFromURL(source, { overwrite });
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
console.log(chalk_1.default.cyan(`Importing personality from file: ${source}`));
|
|
1191
|
+
personality = await this.personalityManager.importFromFile(source, { overwrite });
|
|
1192
|
+
}
|
|
1193
|
+
console.log(this.renderer.renderSuccess(`Imported personality: ${personality.name}`));
|
|
1194
|
+
console.log(chalk_1.default.gray(` ${personality.description}`));
|
|
1195
|
+
}
|
|
1196
|
+
catch (error) {
|
|
1197
|
+
console.error(chalk_1.default.red('Error importing personality:'), error);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
async handlePersonalityExport(args) {
|
|
1201
|
+
if (!this.personalityManager)
|
|
1202
|
+
return;
|
|
1203
|
+
if (args.length < 2) {
|
|
1204
|
+
console.log(chalk_1.default.red('Usage: /personality export <name> <output_file>'));
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
const name = args[0];
|
|
1208
|
+
const outputFile = args[1];
|
|
1209
|
+
try {
|
|
1210
|
+
await this.personalityManager.exportToFile(name, outputFile);
|
|
1211
|
+
console.log(this.renderer.renderSuccess(`Exported personality "${name}" to ${outputFile}`));
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
console.error(chalk_1.default.red('Error exporting personality:'), error);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
handlePersonalitySearch(query) {
|
|
1218
|
+
if (!query) {
|
|
1219
|
+
console.log(chalk_1.default.red('Usage: /personality search <query>'));
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
if (!this.personalityManager)
|
|
1223
|
+
return;
|
|
1224
|
+
const results = this.personalityManager.search(query);
|
|
1225
|
+
if (results.length === 0) {
|
|
1226
|
+
console.log(chalk_1.default.yellow('No matching personalities found.'));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
console.log(chalk_1.default.cyan(`Found ${results.length} matching personalities:\\n`));
|
|
1230
|
+
for (const p of results) {
|
|
1231
|
+
console.log(chalk_1.default.white(` ${p.name}`));
|
|
1232
|
+
console.log(chalk_1.default.gray(` Description: ${p.description}`));
|
|
1233
|
+
console.log();
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
handlePersonalityClear() {
|
|
1237
|
+
if (!this.activePersonality) {
|
|
1238
|
+
console.log(chalk_1.default.yellow('No active personality to clear.'));
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const name = this.activePersonality.name;
|
|
1242
|
+
this.activePersonality = undefined;
|
|
1243
|
+
console.log(this.renderer.renderSuccess(`Cleared active personality: ${name}`));
|
|
1244
|
+
}
|
|
1245
|
+
showContext() {
|
|
1246
|
+
const context = this.conversationManager.getContext();
|
|
1247
|
+
const summary = this.todoManager.getSummary();
|
|
1248
|
+
console.log(chalk_1.default.cyan('Current Context:\n'));
|
|
1249
|
+
console.log(chalk_1.default.gray('Working Directory:'), chalk_1.default.white(context.workingDirectory));
|
|
1250
|
+
console.log(chalk_1.default.gray('Active Files:'), chalk_1.default.white(context.activeFiles.size > 0 ? Array.from(context.activeFiles).join(', ') : '(none)'));
|
|
1251
|
+
console.log(chalk_1.default.gray('Message History:'), chalk_1.default.white(`${context.messages.length} messages`));
|
|
1252
|
+
console.log(chalk_1.default.gray('Task Progress:'), chalk_1.default.white(`${summary.completed}/${summary.total} (${summary.completionPercentage}%)`));
|
|
1253
|
+
}
|
|
1254
|
+
showHelp() {
|
|
1255
|
+
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
1256
|
+
console.log(chalk_1.default.cyan('Available Commands:\n'));
|
|
1257
|
+
console.log(chalk_1.default.white(' /setup '), chalk_1.default.gray('Run interactive setup'));
|
|
1258
|
+
console.log(chalk_1.default.white(' /read <file> '), chalk_1.default.gray('Read a file'));
|
|
1259
|
+
console.log(chalk_1.default.white(' /write <file> <text> '), chalk_1.default.gray('Write content to a file'));
|
|
1260
|
+
console.log(chalk_1.default.white(' /edit <file> <search> <replace>'), chalk_1.default.gray('Edit a file'));
|
|
1261
|
+
console.log(chalk_1.default.white(' /search <query> '), chalk_1.default.gray('Search in files'));
|
|
1262
|
+
console.log(chalk_1.default.white(' /code <query> '), chalk_1.default.gray('Semantic code search'));
|
|
1263
|
+
console.log(chalk_1.default.white(' /ls [path] '), chalk_1.default.gray('List directory contents'));
|
|
1264
|
+
console.log(chalk_1.default.white(' /run <cmd> [--async] '), chalk_1.default.gray('Run a command'));
|
|
1265
|
+
console.log(chalk_1.default.white(' /stop [id] '), chalk_1.default.gray('Stop a running process'));
|
|
1266
|
+
console.log(chalk_1.default.white(' /todo <subcmd> '), chalk_1.default.gray('Manage tasks (add/list/done/delete/summary)'));
|
|
1267
|
+
console.log(chalk_1.default.white(' /mem <subcmd> '), chalk_1.default.gray('Manage memories (add/list/delete)'));
|
|
1268
|
+
console.log(chalk_1.default.white(' /mcp <subcmd> '), chalk_1.default.gray('Manage MCP servers (list/add/remove/connect/disconnect/tools/enable/disable)'));
|
|
1269
|
+
console.log(chalk_1.default.white(' /context '), chalk_1.default.gray('Show current context'));
|
|
1270
|
+
console.log(chalk_1.default.white(' /clear '), chalk_1.default.gray('Clear the screen'));
|
|
1271
|
+
console.log(chalk_1.default.white(' help '), chalk_1.default.gray('Show this help message'));
|
|
1272
|
+
console.log(chalk_1.default.white(' exit/quit '), chalk_1.default.gray('Exit Kaze CLI'));
|
|
1273
|
+
}
|
|
1274
|
+
async sendMessage(message) {
|
|
1275
|
+
const userMessage = {
|
|
1276
|
+
role: 'user',
|
|
1277
|
+
content: message,
|
|
1278
|
+
timestamp: Date.now()
|
|
1279
|
+
};
|
|
1280
|
+
this.tui.appendHistory(`User: ${message}`);
|
|
1281
|
+
this.tui.markDirty();
|
|
1282
|
+
this.tui.render();
|
|
1283
|
+
const contextStr = this.buildContext();
|
|
1284
|
+
const messages = [
|
|
1285
|
+
...this.conversationManager.getRecentMessages(this.config.maxHistory),
|
|
1286
|
+
userMessage
|
|
1287
|
+
];
|
|
1288
|
+
this.conversationManager.addMessage(userMessage);
|
|
1289
|
+
let systemPrompt;
|
|
1290
|
+
if (this.activePersonality) {
|
|
1291
|
+
const personality = this.activePersonality;
|
|
1292
|
+
const capabilitiesSection = `
|
|
1293
|
+
## Your Capabilities
|
|
1294
|
+
|
|
1295
|
+
### File Operations
|
|
1296
|
+
- **read_file**: Read the contents of a file
|
|
1297
|
+
- **edit_file**: Edit a file using search and replace (DIFF MODE - PREFERRED for editing existing files)
|
|
1298
|
+
- **write_file**: Write content to a file (overwrites existing content)
|
|
1299
|
+
- **search_files**: Search for files in the codebase
|
|
1300
|
+
|
|
1301
|
+
### Command Execution
|
|
1302
|
+
- **execute_command**: Run shell commands (install dependencies, run tests, build projects, etc.)
|
|
1303
|
+
`;
|
|
1304
|
+
systemPrompt = `${personality.systemPrompt}
|
|
1305
|
+
|
|
1306
|
+
${capabilitiesSection}
|
|
1307
|
+
|
|
1308
|
+
## Context Information
|
|
1309
|
+
${contextStr}`;
|
|
1310
|
+
}
|
|
1311
|
+
else {
|
|
1312
|
+
systemPrompt = `You are a powerful AI coding assistant that helps users with software engineering tasks. You have access to tools that allow you to interact with the user's codebase.
|
|
1313
|
+
|
|
1314
|
+
## Your Capabilities
|
|
1315
|
+
|
|
1316
|
+
### File Operations
|
|
1317
|
+
- **read_file**: Read the contents of a file
|
|
1318
|
+
- **edit_file**: Edit a file using search and replace (DIFF MODE - PREFERRED for editing existing files)
|
|
1319
|
+
- **write_file**: Write content to a file (overwrites existing content)
|
|
1320
|
+
- **search_files**: Search for files in the codebase
|
|
1321
|
+
|
|
1322
|
+
### Command Execution
|
|
1323
|
+
- **execute_command**: Run shell commands (install dependencies, run tests, build projects, etc.)
|
|
1324
|
+
|
|
1325
|
+
## Guidelines
|
|
1326
|
+
|
|
1327
|
+
1. **Be Proactive**: Take initiative to help users with their tasks. When users ask you to do something, do it rather than just explaining how.
|
|
1328
|
+
|
|
1329
|
+
2. **Default to edit_file**:
|
|
1330
|
+
- **ALWAYS use edit_file (diff mode) as the DEFAULT method for editing existing files**
|
|
1331
|
+
- Only use write_file when creating entirely new files or completely overwriting a file
|
|
1332
|
+
- The edit_file tool provides real-time progress feedback and shows exactly what changes are being made
|
|
1333
|
+
- It shows a detailed diff with line-by-line changes before applying them
|
|
1334
|
+
|
|
1335
|
+
3. **Use Tools Appropriately**:
|
|
1336
|
+
- Read files to understand the codebase before making changes
|
|
1337
|
+
- Use search to find relevant files
|
|
1338
|
+
- Run commands to test your changes (build, test, lint)
|
|
1339
|
+
- Never assume a library exists - check the codebase first
|
|
1340
|
+
|
|
1341
|
+
4. **Follow Conventions**:
|
|
1342
|
+
- Mimic the existing code style and patterns
|
|
1343
|
+
- Use existing libraries and utilities
|
|
1344
|
+
- Follow security best practices (never expose secrets)
|
|
1345
|
+
|
|
1346
|
+
5. **Code Style**: Do not add any comments to code unless explicitly asked.
|
|
1347
|
+
|
|
1348
|
+
6. **Task Management**: Use the todo system to track complex tasks. Create specific, actionable items and update them as you progress.
|
|
1349
|
+
|
|
1350
|
+
7. **Communication**: Be clear and educational. Provide helpful insights while remaining focused on the task.
|
|
1351
|
+
|
|
1352
|
+
## Context Information
|
|
1353
|
+
${contextStr}`;
|
|
1354
|
+
}
|
|
1355
|
+
messages.unshift({
|
|
1356
|
+
role: 'system',
|
|
1357
|
+
content: systemPrompt
|
|
1358
|
+
});
|
|
1359
|
+
const apiMessages = messages.map(msg => ({
|
|
1360
|
+
role: msg.role,
|
|
1361
|
+
content: msg.content,
|
|
1362
|
+
tool_calls: msg.tool_calls,
|
|
1363
|
+
tool_call_id: msg.tool_call_id
|
|
1364
|
+
}));
|
|
1365
|
+
let assistantToolCalls;
|
|
1366
|
+
let fullResponse = '';
|
|
1367
|
+
let hasToolCalls = false;
|
|
1368
|
+
const effectiveTemperature = this.activePersonality?.temperature ?? this.config.temperature;
|
|
1369
|
+
const effectiveMaxTokens = this.activePersonality?.maxTokens ?? this.config.maxTokens;
|
|
1370
|
+
try {
|
|
1371
|
+
this.logger.startThinking();
|
|
1372
|
+
const response = this.client.chatStreamIterable({
|
|
1373
|
+
model: this.config.model,
|
|
1374
|
+
messages: apiMessages,
|
|
1375
|
+
tools: this.config.enableTools ? this.getTools() : undefined,
|
|
1376
|
+
tool_choice: this.config.enableTools ? 'auto' : undefined,
|
|
1377
|
+
stream: true,
|
|
1378
|
+
max_tokens: effectiveMaxTokens,
|
|
1379
|
+
temperature: effectiveTemperature
|
|
1380
|
+
});
|
|
1381
|
+
let firstChunk = true;
|
|
1382
|
+
let assistantResponse = '';
|
|
1383
|
+
for await (const chunk of response) {
|
|
1384
|
+
if (firstChunk) {
|
|
1385
|
+
this.logger.stopThinking();
|
|
1386
|
+
this.tui.appendHistory('Assistant: ');
|
|
1387
|
+
console.log('');
|
|
1388
|
+
}
|
|
1389
|
+
const delta = chunk.choices[0]?.delta;
|
|
1390
|
+
if (delta?.content) {
|
|
1391
|
+
process.stdout.write(delta.content);
|
|
1392
|
+
fullResponse += delta.content;
|
|
1393
|
+
assistantResponse += delta.content;
|
|
1394
|
+
}
|
|
1395
|
+
if (delta?.tool_calls && this.config.enableTools) {
|
|
1396
|
+
hasToolCalls = true;
|
|
1397
|
+
if (!assistantToolCalls) {
|
|
1398
|
+
assistantToolCalls = [];
|
|
1399
|
+
}
|
|
1400
|
+
for (const toolCall of delta.tool_calls) {
|
|
1401
|
+
const existingIndex = assistantToolCalls.findIndex(tc => tc.id === toolCall.id);
|
|
1402
|
+
if (existingIndex >= 0) {
|
|
1403
|
+
assistantToolCalls[existingIndex] = toolCall;
|
|
1404
|
+
}
|
|
1405
|
+
else {
|
|
1406
|
+
assistantToolCalls.push(toolCall);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
firstChunk = false;
|
|
1411
|
+
}
|
|
1412
|
+
console.log('');
|
|
1413
|
+
this.tokensUsed += (message.length + assistantResponse.length);
|
|
1414
|
+
this.tui.setInfo({ tokensUsed: this.tokensUsed });
|
|
1415
|
+
this.tui.appendHistory(assistantResponse);
|
|
1416
|
+
this.tui.markDirty();
|
|
1417
|
+
if (hasToolCalls && assistantToolCalls) {
|
|
1418
|
+
const assistantMessage = {
|
|
1419
|
+
role: 'assistant',
|
|
1420
|
+
content: fullResponse,
|
|
1421
|
+
tool_calls: assistantToolCalls,
|
|
1422
|
+
timestamp: Date.now()
|
|
1423
|
+
};
|
|
1424
|
+
this.conversationManager.addMessage(assistantMessage);
|
|
1425
|
+
for (const toolCall of assistantToolCalls) {
|
|
1426
|
+
const result = await this.executeToolCall(toolCall);
|
|
1427
|
+
this.conversationManager.addMessage({
|
|
1428
|
+
role: 'tool',
|
|
1429
|
+
tool_call_id: toolCall.id,
|
|
1430
|
+
content: JSON.stringify(result)
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
let moreToolCalls = true;
|
|
1434
|
+
while (moreToolCalls) {
|
|
1435
|
+
this.logger.startThinking();
|
|
1436
|
+
const followUpMessages = [
|
|
1437
|
+
...this.conversationManager.getRecentMessages(this.config.maxHistory)
|
|
1438
|
+
];
|
|
1439
|
+
const followUpApiMessages = followUpMessages.map(msg => ({
|
|
1440
|
+
role: msg.role,
|
|
1441
|
+
content: msg.content,
|
|
1442
|
+
tool_calls: msg.tool_calls,
|
|
1443
|
+
tool_call_id: msg.tool_call_id
|
|
1444
|
+
}));
|
|
1445
|
+
const followUpResponse = this.client.chatStreamIterable({
|
|
1446
|
+
model: this.config.model,
|
|
1447
|
+
messages: followUpApiMessages,
|
|
1448
|
+
tools: this.config.enableTools ? this.getTools() : undefined,
|
|
1449
|
+
tool_choice: this.config.enableTools ? 'auto' : undefined,
|
|
1450
|
+
stream: true
|
|
1451
|
+
});
|
|
1452
|
+
let followUpToolCalls;
|
|
1453
|
+
fullResponse = '';
|
|
1454
|
+
let firstFollowUpChunk = true;
|
|
1455
|
+
for await (const chunk of followUpResponse) {
|
|
1456
|
+
const delta = chunk.choices[0]?.delta;
|
|
1457
|
+
if (firstFollowUpChunk) {
|
|
1458
|
+
this.logger.stopThinking();
|
|
1459
|
+
console.log('');
|
|
1460
|
+
firstFollowUpChunk = false;
|
|
1461
|
+
}
|
|
1462
|
+
if (delta?.content) {
|
|
1463
|
+
process.stdout.write(delta.content);
|
|
1464
|
+
fullResponse += delta.content;
|
|
1465
|
+
}
|
|
1466
|
+
if (delta?.tool_calls && this.config.enableTools) {
|
|
1467
|
+
moreToolCalls = true;
|
|
1468
|
+
if (!followUpToolCalls) {
|
|
1469
|
+
followUpToolCalls = [];
|
|
1470
|
+
}
|
|
1471
|
+
for (const toolCall of delta.tool_calls) {
|
|
1472
|
+
const existingIndex = followUpToolCalls.findIndex(tc => tc.id === toolCall.id);
|
|
1473
|
+
if (existingIndex >= 0) {
|
|
1474
|
+
followUpToolCalls[existingIndex] = toolCall;
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
followUpToolCalls.push(toolCall);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
console.log('');
|
|
1483
|
+
if (followUpToolCalls && followUpToolCalls.length > 0) {
|
|
1484
|
+
const followUpAssistantMessage = {
|
|
1485
|
+
role: 'assistant',
|
|
1486
|
+
content: fullResponse,
|
|
1487
|
+
tool_calls: followUpToolCalls,
|
|
1488
|
+
timestamp: Date.now()
|
|
1489
|
+
};
|
|
1490
|
+
this.conversationManager.addMessage(followUpAssistantMessage);
|
|
1491
|
+
for (const toolCall of followUpToolCalls) {
|
|
1492
|
+
const result = await this.executeToolCall(toolCall);
|
|
1493
|
+
this.conversationManager.addMessage({
|
|
1494
|
+
role: 'tool',
|
|
1495
|
+
tool_call_id: toolCall.id,
|
|
1496
|
+
content: JSON.stringify(result)
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
moreToolCalls = false;
|
|
1502
|
+
const finalAssistantMessage = {
|
|
1503
|
+
role: 'assistant',
|
|
1504
|
+
content: fullResponse,
|
|
1505
|
+
timestamp: Date.now()
|
|
1506
|
+
};
|
|
1507
|
+
this.conversationManager.addMessage(finalAssistantMessage);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
else {
|
|
1512
|
+
console.log('');
|
|
1513
|
+
const assistantMessage = {
|
|
1514
|
+
role: 'assistant',
|
|
1515
|
+
content: fullResponse,
|
|
1516
|
+
timestamp: Date.now()
|
|
1517
|
+
};
|
|
1518
|
+
this.conversationManager.addMessage(assistantMessage);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
catch (error) {
|
|
1522
|
+
this.logger.stopThinking();
|
|
1523
|
+
this.logger.error(String(error));
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
buildContext() {
|
|
1527
|
+
const memories = [...this.memoryManager.getUserMemories(), ...this.memoryManager.getProjectMemories()];
|
|
1528
|
+
const activeFiles = Array.from(this.conversationManager.getContext().activeFiles);
|
|
1529
|
+
const summary = this.todoManager.getSummary();
|
|
1530
|
+
const parts = [];
|
|
1531
|
+
if (this.config.workingDirectory) {
|
|
1532
|
+
parts.push(`Working Directory: ${this.config.workingDirectory}`);
|
|
1533
|
+
}
|
|
1534
|
+
if (activeFiles.length > 0) {
|
|
1535
|
+
parts.push(`Active Files: ${activeFiles.join(', ')}`);
|
|
1536
|
+
}
|
|
1537
|
+
if (summary.total > 0) {
|
|
1538
|
+
parts.push(`Task Progress: ${summary.completed}/${summary.total} (${summary.completionPercentage}%)`);
|
|
1539
|
+
}
|
|
1540
|
+
if (memories.length > 0) {
|
|
1541
|
+
parts.push('\nRelevant Memories:');
|
|
1542
|
+
memories.forEach(mem => {
|
|
1543
|
+
parts.push(`- [${mem.scope}][${mem.category}] ${mem.title}`);
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
return parts.length > 0 ? parts.join('\n') : '';
|
|
1547
|
+
}
|
|
1548
|
+
getTools() {
|
|
1549
|
+
const tools = [
|
|
1550
|
+
{
|
|
1551
|
+
type: 'function',
|
|
1552
|
+
function: {
|
|
1553
|
+
name: 'read_file',
|
|
1554
|
+
description: 'Read the contents of a file',
|
|
1555
|
+
parameters: {
|
|
1556
|
+
type: 'object',
|
|
1557
|
+
properties: {
|
|
1558
|
+
file_path: {
|
|
1559
|
+
type: 'string',
|
|
1560
|
+
description: 'The absolute path to the file to read'
|
|
1561
|
+
}
|
|
1562
|
+
},
|
|
1563
|
+
required: ['file_path']
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
type: 'function',
|
|
1569
|
+
function: {
|
|
1570
|
+
name: 'edit_file',
|
|
1571
|
+
description: '✨ DEFAULT EDITING METHOD - Edit a file using search and replace with real-time progress tracking. This provides visual feedback showing the percentage complete and displays a detailed diff of changes. This is the PREFERRED method for editing existing files. When you choose to use this tool to edit an existing file, you MUST follow the SEARCH/REPLACE Rules to set the old_str and new_str parameters: 1. The old_str is the SEARCH section that should be a contiguous chunk of lines to search for in the existing source code. 2. The new_str is the REPLACE section that should be lines to replace into the source code. 3. The REPLACE section MUST be different from the SEARCH section. This tool will only replace the first match occurrence of the SEARCH section. Include enough lines in the SEARCH section to uniquely match the set of lines that need to change. Keep your SEARCH and REPLACE sections concise. Include just the changing lines, and a few surrounding lines if needed for uniqueness. Only create SEARCH/REPLACE sections for files that the user has added to the chat. If you want to move code within a file, you need to make two separate edit operations: delete the original code chunk and then insert it in another location.',
|
|
1572
|
+
parameters: {
|
|
1573
|
+
type: 'object',
|
|
1574
|
+
properties: {
|
|
1575
|
+
file_path: {
|
|
1576
|
+
type: 'string',
|
|
1577
|
+
description: 'The absolute path to the file to edit'
|
|
1578
|
+
},
|
|
1579
|
+
old_str: {
|
|
1580
|
+
type: 'string',
|
|
1581
|
+
description: 'The SEARCH section that should be a contiguous chunk of lines to search for in the existing source code'
|
|
1582
|
+
},
|
|
1583
|
+
new_str: {
|
|
1584
|
+
type: 'string',
|
|
1585
|
+
description: 'The REPLACE section that should be lines to replace into the source code'
|
|
1586
|
+
}
|
|
1587
|
+
},
|
|
1588
|
+
required: ['file_path', 'old_str', 'new_str']
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
type: 'function',
|
|
1594
|
+
function: {
|
|
1595
|
+
name: 'write_file',
|
|
1596
|
+
description: 'Write content to a file (overwrites existing content). ALWAYS prefer editing existing files in the codebase. NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.',
|
|
1597
|
+
parameters: {
|
|
1598
|
+
type: 'object',
|
|
1599
|
+
properties: {
|
|
1600
|
+
file_path: {
|
|
1601
|
+
type: 'string',
|
|
1602
|
+
description: 'The absolute path to the file to write'
|
|
1603
|
+
},
|
|
1604
|
+
content: {
|
|
1605
|
+
type: 'string',
|
|
1606
|
+
description: 'The content to write to the file'
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
required: ['file_path', 'content']
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
type: 'function',
|
|
1615
|
+
function: {
|
|
1616
|
+
name: 'search_files',
|
|
1617
|
+
description: 'Search for files in the codebase by filename or content. Use this when you need to find specific files by name or search within file contents.',
|
|
1618
|
+
parameters: {
|
|
1619
|
+
type: 'object',
|
|
1620
|
+
properties: {
|
|
1621
|
+
query: {
|
|
1622
|
+
type: 'string',
|
|
1623
|
+
description: 'The search query (filename pattern or content to search within files)'
|
|
1624
|
+
},
|
|
1625
|
+
search_mode: {
|
|
1626
|
+
type: 'string',
|
|
1627
|
+
enum: ['filename', 'content', 'both'],
|
|
1628
|
+
description: 'Search mode: filename (find files by name), content (search file contents), or both'
|
|
1629
|
+
}
|
|
1630
|
+
},
|
|
1631
|
+
required: ['query']
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
type: 'function',
|
|
1637
|
+
function: {
|
|
1638
|
+
name: 'execute_command',
|
|
1639
|
+
description: 'Run shell commands (install dependencies, run tests, build projects, etc.). Do NOT use this for file editing operations - use edit_file or write_file instead.',
|
|
1640
|
+
parameters: {
|
|
1641
|
+
type: 'object',
|
|
1642
|
+
properties: {
|
|
1643
|
+
command: {
|
|
1644
|
+
type: 'string',
|
|
1645
|
+
description: 'The shell command to execute'
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
required: ['command']
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1652
|
+
{
|
|
1653
|
+
type: 'function',
|
|
1654
|
+
function: {
|
|
1655
|
+
name: 'search_code',
|
|
1656
|
+
description: 'Search the codebase using semantic search',
|
|
1657
|
+
parameters: {
|
|
1658
|
+
type: 'object',
|
|
1659
|
+
properties: {
|
|
1660
|
+
query: {
|
|
1661
|
+
type: 'string',
|
|
1662
|
+
description: 'The query to search for in the codebase'
|
|
1663
|
+
}
|
|
1664
|
+
},
|
|
1665
|
+
required: ['query']
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
];
|
|
1670
|
+
for (const [serverName, serverTools] of this.mcpToolsCache) {
|
|
1671
|
+
tools.push(...serverTools);
|
|
1672
|
+
}
|
|
1673
|
+
return tools;
|
|
1674
|
+
}
|
|
1675
|
+
async executeToolCall(toolCall) {
|
|
1676
|
+
const { name, arguments: argsString } = toolCall.function;
|
|
1677
|
+
try {
|
|
1678
|
+
const args = JSON.parse(argsString);
|
|
1679
|
+
let result;
|
|
1680
|
+
const startTime = Date.now();
|
|
1681
|
+
switch (name) {
|
|
1682
|
+
case 'read_file':
|
|
1683
|
+
this.logger.toolAction('Read File', 'Read the contents of a file', { file: args.file_path });
|
|
1684
|
+
result = await this.fileTool.readFile(args.file_path);
|
|
1685
|
+
break;
|
|
1686
|
+
case 'edit_file':
|
|
1687
|
+
this.logger.toolAction('Edit File', 'Edit a file using search and replace', { file: args.file_path });
|
|
1688
|
+
this.fileTool.onEditProgress((step, progress, total, message) => {
|
|
1689
|
+
this.logger.editProgress(step, progress, total, message);
|
|
1690
|
+
});
|
|
1691
|
+
const originalResult = await this.fileTool.readFile(args.file_path);
|
|
1692
|
+
const original = originalResult.content;
|
|
1693
|
+
const editResult = await this.fileTool.editFile(args.file_path, args.old_str, args.new_str);
|
|
1694
|
+
this.logger.fileEdit(original, original.replace(args.old_str, args.new_str), args.file_path);
|
|
1695
|
+
// Calculate lines added/removed
|
|
1696
|
+
const originalLines = original.split('\n');
|
|
1697
|
+
const newLines = original.replace(args.old_str, args.new_str).split('\n');
|
|
1698
|
+
const linesAdded = Math.max(0, newLines.length - originalLines.length);
|
|
1699
|
+
const linesRemoved = Math.max(0, originalLines.length - newLines.length);
|
|
1700
|
+
this.logger.editFileAction(args.file_path, linesAdded, linesRemoved);
|
|
1701
|
+
result = editResult;
|
|
1702
|
+
this.fileTool.onEditProgress(null);
|
|
1703
|
+
break;
|
|
1704
|
+
case 'write_file':
|
|
1705
|
+
this.logger.toolAction('Write File', 'Write content to a file', { file: args.file_path });
|
|
1706
|
+
try {
|
|
1707
|
+
const exists = await this.fileTool.fileExists(args.file_path);
|
|
1708
|
+
if (exists) {
|
|
1709
|
+
const originalResult = await this.fileTool.readFile(args.file_path);
|
|
1710
|
+
const original = originalResult.content;
|
|
1711
|
+
this.logger.fileEdit(original, args.content, args.file_path);
|
|
1712
|
+
}
|
|
1713
|
+
result = await this.fileTool.writeFile(args.file_path, args.content);
|
|
1714
|
+
const lines = args.content.split('\n').length;
|
|
1715
|
+
const size = Buffer.byteLength(args.content, 'utf8');
|
|
1716
|
+
this.logger.writeFileAction(args.file_path, lines, size);
|
|
1717
|
+
}
|
|
1718
|
+
catch (e) {
|
|
1719
|
+
result = await this.fileTool.writeFile(args.file_path, args.content);
|
|
1720
|
+
const lines = args.content.split('\n').length;
|
|
1721
|
+
const size = Buffer.byteLength(args.content, 'utf8');
|
|
1722
|
+
this.logger.writeFileAction(args.file_path, lines, size);
|
|
1723
|
+
}
|
|
1724
|
+
break;
|
|
1725
|
+
case 'search_files':
|
|
1726
|
+
this.logger.toolAction('Search Files', 'Search for files by name or content', { query: args.query, mode: args.search_mode });
|
|
1727
|
+
result = await this.fileTool.searchFiles(args.query, {
|
|
1728
|
+
path: this.config.workingDirectory,
|
|
1729
|
+
searchMode: args.search_mode || 'filename'
|
|
1730
|
+
});
|
|
1731
|
+
const resultsFound = result.reduce((acc, r) => acc + r.matches.length, 0);
|
|
1732
|
+
this.logger.searchFilesAction(args.query, args.search_mode || 'filename', resultsFound, result.length);
|
|
1733
|
+
break;
|
|
1734
|
+
case 'execute_command':
|
|
1735
|
+
this.logger.toolAction('Execute Command', 'Run shell commands', { command: args.command });
|
|
1736
|
+
const execResult = await this.terminalTool.executeCommand(args.command, [], {
|
|
1737
|
+
cwd: this.config.workingDirectory
|
|
1738
|
+
});
|
|
1739
|
+
const duration = Date.now() - startTime;
|
|
1740
|
+
this.logger.executeCommandAction(args.command, execResult.exitCode, duration);
|
|
1741
|
+
result = execResult;
|
|
1742
|
+
break;
|
|
1743
|
+
case 'search_code':
|
|
1744
|
+
this.logger.toolAction('Search Code', 'Search codebase using semantic search', { query: args.query });
|
|
1745
|
+
result = await this.codeSearchTool.semanticSearch(args.query);
|
|
1746
|
+
this.logger.searchCodeAction(args.query, result.length);
|
|
1747
|
+
break;
|
|
1748
|
+
default:
|
|
1749
|
+
if (this.mcpManager && name.includes('_')) {
|
|
1750
|
+
const [serverName, ...toolParts] = name.split('_');
|
|
1751
|
+
const toolName = toolParts.join('_');
|
|
1752
|
+
const displayName = toolName.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
1753
|
+
this.logger.toolAction(serverName, displayName, args);
|
|
1754
|
+
try {
|
|
1755
|
+
result = await this.mcpManager.callTool(serverName, toolName, args);
|
|
1756
|
+
}
|
|
1757
|
+
catch (error) {
|
|
1758
|
+
// MCP tool call failed - return error instead of crashing
|
|
1759
|
+
this.logger.error(`MCP tool call failed for ${serverName}.${toolName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1760
|
+
result = { error: `MCP tool call failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
else {
|
|
1764
|
+
this.logger.toolAction('Unknown', name, args);
|
|
1765
|
+
result = { error: `Unknown tool: ${name}` };
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return result;
|
|
1769
|
+
}
|
|
1770
|
+
catch (error) {
|
|
1771
|
+
return { error: String(error) };
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async handleToolCall(toolCall) {
|
|
1775
|
+
const { name } = toolCall.function;
|
|
1776
|
+
console.log(chalk_1.default.gray(`\n[Tool: ${name}]`));
|
|
1777
|
+
const result = await this.executeToolCall(toolCall);
|
|
1778
|
+
console.log(chalk_1.default.gray(JSON.stringify(result, null, 2)));
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
exports.REPL = REPL;
|
|
1782
|
+
//# sourceMappingURL=repl.js.map
|