@pheem49/mint 1.4.2 → 1.5.1
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/GUIDE_TH.md +113 -0
- package/README.md +267 -78
- package/assets/CLI_Screen.png +0 -0
- package/main.js +76 -890
- package/mint-cli-logic.js +3 -107
- package/mint-cli.js +594 -29
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json +15 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
- package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +23 -0
- package/package.json +37 -4
- package/src/AI_Brain/Gemini_API.js +223 -65
- package/src/AI_Brain/autonomous_brain.js +11 -0
- package/src/AI_Brain/behavior_memory.js +26 -5
- package/src/AI_Brain/headless_agent.js +4 -0
- package/src/AI_Brain/knowledge_base.js +61 -8
- package/src/AI_Brain/memory_store.js +354 -10
- package/src/Automation_Layer/file_operations.js +1 -1
- package/src/CLI/chat_router.js +20 -7
- package/src/CLI/chat_ui.js +596 -825
- package/src/CLI/code_agent.js +347 -56
- package/src/CLI/gmail_auth.js +210 -0
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/list_features.js +2 -0
- package/src/CLI/onboarding.js +364 -55
- package/src/CLI/updater.js +210 -0
- package/src/Channels/brave_search_bridge.js +35 -0
- package/src/Channels/discord_bridge.js +68 -0
- package/src/Channels/google_search_bridge.js +38 -0
- package/src/Channels/line_bridge.js +60 -0
- package/src/Channels/slack_bridge.js +53 -0
- package/src/Channels/telegram_bridge.js +49 -0
- package/src/Channels/whatsapp_bridge.js +55 -0
- package/src/Command_Parser/parser.js +12 -1
- package/src/Plugins/gmail.js +251 -0
- package/src/Plugins/google_calendar.js +245 -19
- package/src/Plugins/notion.js +256 -0
- package/src/System/action_executor.js +178 -0
- package/src/System/bridge_manager.js +76 -0
- package/src/System/chat_history_manager.js +23 -5
- package/src/System/config_manager.js +71 -7
- package/src/System/custom_workflows.js +31 -2
- package/src/System/google_tts_urls.js +51 -0
- package/src/System/granular_automation.js +122 -53
- package/src/System/ipc_handlers.js +238 -0
- package/src/System/proactive_loop.js +153 -0
- package/src/System/safety_manager.js +273 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/screen_capture.js +175 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/task_manager.js +15 -5
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +212 -0
- package/src/UI/live2d_manager.js +368 -0
- package/src/UI/renderer.js +208 -24
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +14 -4
- package/src/UI/styles.css +466 -32
- package/.codex +0 -0
- package/docs/assets/Agent_Mint.png +0 -0
- package/docs/assets/CLI_Screen.png +0 -0
- package/docs/assets/Settings.png +0 -0
- package/docs/assets/icon.png +0 -0
- package/docs/index.html +0 -132
- package/docs/style.css +0 -579
- package/index.html +0 -16
- package/src/UI/index.html +0 -126
- package/tech_news.txt +0 -3
- package/test_knowledge.txt +0 -3
- package/tests/agent_orchestrator.test.js +0 -41
- package/tests/chat_router.test.js +0 -42
- package/tests/code_agent.test.js +0 -69
- package/tests/config_manager.test.js +0 -141
- package/tests/docker.test.js +0 -46
- package/tests/file_operations.test.js +0 -57
- package/tests/memory_store.test.js +0 -185
- package/tests/provider_routing.test.js +0 -67
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- package/tests/workspace_manager.test.js +0 -56
package/mint-cli.js
CHANGED
|
@@ -9,6 +9,8 @@ process.emit = function (name, data, ...args) {
|
|
|
9
9
|
return originalEmit.apply(process, [name, data, ...args]);
|
|
10
10
|
};
|
|
11
11
|
const { Command } = require('commander');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
12
14
|
const { handleChat, handleGeminiChatStream, resetChat, refreshApiKeyFromConfig, getChatTranscript } = require('./src/AI_Brain/Gemini_API');
|
|
13
15
|
const agentOrchestrator = require('./src/AI_Brain/agent_orchestrator');
|
|
14
16
|
const workspaceManager = require('./src/CLI/workspace_manager');
|
|
@@ -21,18 +23,31 @@ const { displayFeatures } = require('./src/CLI/list_features');
|
|
|
21
23
|
const { readConfig, writeConfig } = require('./src/System/config_manager');
|
|
22
24
|
const { executeCodeTask } = require('./src/CLI/code_agent');
|
|
23
25
|
const { detectCodeIntent, runChatRoutedTask } = require('./src/CLI/chat_router');
|
|
26
|
+
const memoryStore = require('./src/AI_Brain/memory_store');
|
|
24
27
|
const readline = require('readline');
|
|
25
28
|
const { createChatUI } = require('./src/CLI/chat_ui');
|
|
29
|
+
const { runUpdate, runStartupAutoUpdate, shouldRunAutoUpdate } = require('./src/CLI/updater');
|
|
30
|
+
const { runGmailAuth } = require('./src/CLI/gmail_auth');
|
|
31
|
+
const { loadImageAsDataUri, loadClipboardImageAsDataUri } = require('./src/CLI/image_input');
|
|
26
32
|
|
|
27
33
|
// Startup Info
|
|
28
34
|
const startupConfig = readConfig();
|
|
29
|
-
const
|
|
35
|
+
const startupProvider = startupConfig.aiProvider || 'gemini';
|
|
36
|
+
const startupModel = startupProvider === 'openai'
|
|
37
|
+
? (startupConfig.openaiModel || 'gpt-4o')
|
|
38
|
+
: startupProvider === 'anthropic'
|
|
39
|
+
? (startupConfig.anthropicModel || 'claude-3-5-sonnet-latest')
|
|
40
|
+
: startupProvider === 'local_openai'
|
|
41
|
+
? (startupConfig.localModelName || 'local-model')
|
|
42
|
+
: startupProvider === 'ollama'
|
|
43
|
+
? (startupConfig.ollamaModel || 'llama3:latest')
|
|
44
|
+
: (startupConfig.geminiModel || 'gemini-2.5-flash');
|
|
30
45
|
const startupNow = new Date();
|
|
31
46
|
const startupTime = startupNow.toLocaleString('th-TH', {
|
|
32
47
|
day: '2-digit', month: '2-digit', year: 'numeric',
|
|
33
48
|
hour: '2-digit', minute: '2-digit', hour12: false
|
|
34
49
|
}).replace(',', '');
|
|
35
|
-
console.log(`\x1b[38;5;121m[Mint] v${pkg.version} | ${startupTime} | Active
|
|
50
|
+
console.log(`\x1b[38;5;121m[Mint] v${pkg.version} | ${startupTime} | Active AI: ${startupProvider} • ${startupModel}\x1b[0m`);
|
|
36
51
|
|
|
37
52
|
// ANSI Colors
|
|
38
53
|
const colors = {
|
|
@@ -45,6 +60,22 @@ const colors = {
|
|
|
45
60
|
yellow: "\x1b[33m"
|
|
46
61
|
};
|
|
47
62
|
|
|
63
|
+
let isExiting = false;
|
|
64
|
+
|
|
65
|
+
function exitWithGoodbye(code = 0) {
|
|
66
|
+
if (isExiting) return;
|
|
67
|
+
isExiting = true;
|
|
68
|
+
|
|
69
|
+
process.stdout.write('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l');
|
|
70
|
+
process.stdout.write('\x1b[?25h');
|
|
71
|
+
console.log(`\n${colors.pink}Goodbye! See you again soon!${colors.reset}\n`);
|
|
72
|
+
process.exit(code);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
process.once('SIGINT', () => {
|
|
76
|
+
exitWithGoodbye(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
48
79
|
function formatProgress(info) {
|
|
49
80
|
if (typeof info === 'string') return `${colors.gray}[Mint Code] ${info}${colors.reset}`;
|
|
50
81
|
|
|
@@ -77,6 +108,68 @@ function formatProgress(info) {
|
|
|
77
108
|
return ` ${icon} ${colors.bright}${label}${colors.reset} ${color}${content}${colors.reset}`;
|
|
78
109
|
}
|
|
79
110
|
|
|
111
|
+
function formatMemoryInteractions(interactions, title = 'Remembered interactions') {
|
|
112
|
+
if (!Array.isArray(interactions) || interactions.length === 0) {
|
|
113
|
+
return `${title}:\n(no memories found)`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const lines = [`${title}:`];
|
|
117
|
+
interactions.forEach((item, index) => {
|
|
118
|
+
const when = item.created_at ? ` (${item.created_at})` : '';
|
|
119
|
+
const id = item.id ? `#${item.id} ` : '';
|
|
120
|
+
lines.push(`${index + 1}. ${id}User${when}: ${item.user_text}`);
|
|
121
|
+
lines.push(` Mint: ${item.ai_text}`);
|
|
122
|
+
});
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
127
|
+
|
|
128
|
+
function splitResponseSentences(text) {
|
|
129
|
+
const normalized = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
130
|
+
if (!normalized) return [];
|
|
131
|
+
|
|
132
|
+
const sentences = [];
|
|
133
|
+
let buffer = '';
|
|
134
|
+
for (const char of normalized) {
|
|
135
|
+
buffer += char;
|
|
136
|
+
if (/[.!?。!?…\n]/u.test(char)) {
|
|
137
|
+
const sentence = buffer.trim();
|
|
138
|
+
if (sentence) sentences.push(sentence);
|
|
139
|
+
buffer = '';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const rest = buffer.trim();
|
|
144
|
+
if (rest) sentences.push(rest);
|
|
145
|
+
return sentences.length > 0 ? sentences : [normalized];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function learnSkillFile(filePath) {
|
|
149
|
+
const targetPath = path.resolve(process.cwd(), filePath);
|
|
150
|
+
if (!fs.existsSync(targetPath)) {
|
|
151
|
+
throw new Error(`File not found: ${targetPath}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const stat = fs.statSync(targetPath);
|
|
155
|
+
if (!stat.isFile()) {
|
|
156
|
+
throw new Error(`Path is not a file: ${targetPath}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const ext = path.extname(targetPath).toLowerCase();
|
|
160
|
+
if (ext !== '.md' && ext !== '.txt') {
|
|
161
|
+
throw new Error('Mint learn currently supports .md and .txt files only.');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const maxBytes = 256 * 1024;
|
|
165
|
+
if (stat.size > maxBytes) {
|
|
166
|
+
throw new Error(`File is too large (${stat.size} bytes). Limit is ${maxBytes} bytes.`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const content = fs.readFileSync(targetPath, 'utf8');
|
|
170
|
+
return memoryStore.addLearnedSkill(path.basename(targetPath), targetPath, content);
|
|
171
|
+
}
|
|
172
|
+
|
|
80
173
|
const program = new Command();
|
|
81
174
|
|
|
82
175
|
program
|
|
@@ -84,13 +177,37 @@ program
|
|
|
84
177
|
.description('Mint - Your Personal AI Assistant CLI')
|
|
85
178
|
.version(pkg.version);
|
|
86
179
|
|
|
180
|
+
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
181
|
+
if (actionCommand.name() === 'update' || process.env.MINT_SKIP_AUTO_UPDATE === '1') {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const config = readConfig();
|
|
186
|
+
if (config.enableAutoUpdate === false) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!shouldRunAutoUpdate(config)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`${colors.gray}[Mint Update] Checking for updates...${colors.reset}`);
|
|
195
|
+
const result = await runStartupAutoUpdate(config, writeConfig);
|
|
196
|
+
if (result.status === 'updated') {
|
|
197
|
+
console.log(`${colors.mint}[Mint Update] ${result.message}${colors.reset}`);
|
|
198
|
+
} else if (result.status === 'error') {
|
|
199
|
+
console.log(`${colors.gray}[Mint Update] ${result.message}${colors.reset}`);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
87
203
|
// Chat Command (Interactive Mode)
|
|
88
204
|
program
|
|
89
205
|
.command('chat', { isDefault: true })
|
|
90
206
|
.description('Start interactive chat session with Mint')
|
|
91
207
|
.argument('[message]', 'Initial message to send to Mint')
|
|
92
|
-
.
|
|
93
|
-
|
|
208
|
+
.option('-i, --image <path>', 'Attach an image file to the initial message')
|
|
209
|
+
.action(async (message, options) => {
|
|
210
|
+
await startInteractiveChat(message, { imagePath: options.image });
|
|
94
211
|
});
|
|
95
212
|
|
|
96
213
|
// Onboard Command
|
|
@@ -124,6 +241,57 @@ program
|
|
|
124
241
|
displayFeatures();
|
|
125
242
|
});
|
|
126
243
|
|
|
244
|
+
program
|
|
245
|
+
.command('learn')
|
|
246
|
+
.description('Read a local markdown/text file and remember it as a Mint skill')
|
|
247
|
+
.argument('[filePath]', 'Path to a .md or .txt skill/instruction file')
|
|
248
|
+
.option('--delete <idOrPathOrName>', 'Delete a learned skill by id, path, or name')
|
|
249
|
+
.option('--list', 'List learned skills')
|
|
250
|
+
.action((filePath, options) => {
|
|
251
|
+
try {
|
|
252
|
+
if (options.list) {
|
|
253
|
+
const skills = memoryStore.getLearnedSkills(50);
|
|
254
|
+
if (skills.length === 0) {
|
|
255
|
+
console.log(`\n${colors.gray}No learned skills stored.${colors.reset}\n`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
console.log(`\n${colors.bright}Learned Skills:${colors.reset}`);
|
|
259
|
+
skills.forEach(skill => {
|
|
260
|
+
console.log(`${colors.mint}#${skill.id}${colors.reset} ${skill.name}`);
|
|
261
|
+
console.log(` ${colors.gray}${skill.source_path}${colors.reset}`);
|
|
262
|
+
});
|
|
263
|
+
console.log('');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (options.delete) {
|
|
268
|
+
const deleted = memoryStore.deleteLearnedSkill(options.delete);
|
|
269
|
+
if (deleted > 0) {
|
|
270
|
+
console.log(`\n${colors.mint}✓${colors.reset} Deleted learned skill: ${options.delete}\n`);
|
|
271
|
+
} else {
|
|
272
|
+
console.log(`\n${colors.pink}✗${colors.reset} Learned skill not found: ${options.delete}\n`);
|
|
273
|
+
process.exitCode = 1;
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!filePath) {
|
|
279
|
+
throw new Error('Usage: mint learn <path-to-skill.md>');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const learned = learnSkillFile(filePath);
|
|
283
|
+
console.log(`\n${colors.mint}✓${colors.reset} Learned skill: ${learned.name}`);
|
|
284
|
+
console.log(`${colors.gray}Path: ${learned.source_path}${colors.reset}`);
|
|
285
|
+
if (learned.stored_length < learned.content_length) {
|
|
286
|
+
console.log(`${colors.gray}Stored first ${learned.stored_length} of ${learned.content_length} characters.${colors.reset}`);
|
|
287
|
+
}
|
|
288
|
+
console.log('');
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.error(`\n${colors.pink}Learn failed:${colors.reset} ${error.message}\n`);
|
|
291
|
+
process.exitCode = 1;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
127
295
|
// Task Command (Autonomous Background Task)
|
|
128
296
|
program
|
|
129
297
|
.command('task')
|
|
@@ -139,6 +307,32 @@ program
|
|
|
139
307
|
console.log(`${colors.gray}You will receive a notification when it's done.${colors.reset}\n`);
|
|
140
308
|
});
|
|
141
309
|
|
|
310
|
+
program
|
|
311
|
+
.command('update')
|
|
312
|
+
.description('Check for and install the latest Mint CLI version from npm')
|
|
313
|
+
.option('--check', 'Only check whether an update is available')
|
|
314
|
+
.option('--dry-run', 'Show the npm update operation without installing')
|
|
315
|
+
.action(async (options) => {
|
|
316
|
+
console.log(`\n${colors.mint}${colors.bright}[Mint Update]${colors.reset} Checking npm for updates...`);
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const result = await runUpdate({
|
|
320
|
+
checkOnly: options.check === true,
|
|
321
|
+
dryRun: options.dryRun === true
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const color = result.status === 'error' ? colors.pink : colors.mint;
|
|
325
|
+
console.log(`${color}${result.message}${colors.reset}\n`);
|
|
326
|
+
|
|
327
|
+
if (result.status === 'error') {
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
}
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(`${colors.pink}Update failed: ${error.message}${colors.reset}\n`);
|
|
332
|
+
process.exitCode = 1;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
142
336
|
program
|
|
143
337
|
.command('mcp')
|
|
144
338
|
.description('Manage MCP (Model Context Protocol) servers')
|
|
@@ -212,17 +406,51 @@ program
|
|
|
212
406
|
})
|
|
213
407
|
);
|
|
214
408
|
|
|
409
|
+
program
|
|
410
|
+
.command('gmail')
|
|
411
|
+
.description('Manage Gmail integration')
|
|
412
|
+
.addCommand(new Command('auth')
|
|
413
|
+
.description('Open Google OAuth login and save a Gmail refresh token')
|
|
414
|
+
.option('--port <port>', 'Local callback port, defaults to a random available port')
|
|
415
|
+
.option('--no-open', 'Print the auth link without opening a browser')
|
|
416
|
+
.action(async (options) => {
|
|
417
|
+
try {
|
|
418
|
+
const result = await runGmailAuth({
|
|
419
|
+
port: options.port ? Number(options.port) : 0,
|
|
420
|
+
openBrowser: options.open,
|
|
421
|
+
logger: console
|
|
422
|
+
});
|
|
423
|
+
console.log(`\n${colors.mint}✓${colors.reset} Gmail connected for ${result.userId}. Refresh token saved.`);
|
|
424
|
+
console.log(`${colors.gray}Scopes: ${result.scopes.join(', ')}${colors.reset}\n`);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error(`\n${colors.pink}Gmail auth failed:${colors.reset} ${error.message}\n`);
|
|
427
|
+
process.exitCode = 1;
|
|
428
|
+
}
|
|
429
|
+
})
|
|
430
|
+
);
|
|
431
|
+
|
|
215
432
|
program
|
|
216
433
|
.command('code')
|
|
217
434
|
.description('Run Mint in workspace-aware coding mode for the current project')
|
|
218
435
|
.argument('<task>', 'Coding task to execute in the current working directory')
|
|
219
|
-
.
|
|
436
|
+
.option('-i, --image <path>', 'Attach an image file as context for the coding task')
|
|
437
|
+
.action(async (task, options) => {
|
|
220
438
|
console.log(`\n${colors.mint}${colors.bright}[Mint Code]${colors.reset} Workspace: ${process.cwd()}`);
|
|
221
|
-
console.log(`${colors.gray}[Mint Code] Task: ${task}${colors.reset}\n`);
|
|
222
439
|
|
|
223
440
|
try {
|
|
224
|
-
|
|
441
|
+
let effectiveTask = task;
|
|
442
|
+
let image = null;
|
|
443
|
+
if (options.image) {
|
|
444
|
+
image = loadImageAsDataUri(options.image);
|
|
445
|
+
console.log(`${colors.gray}[Mint Code] Image: ${image.path}${colors.reset}`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
console.log(`${colors.gray}[Mint Code] Task: ${task}${colors.reset}\n`);
|
|
449
|
+
|
|
450
|
+
const result = await executeCodeTask(effectiveTask, {
|
|
225
451
|
cwd: process.cwd(),
|
|
452
|
+
imageDataUri: image ? image.dataUri : null,
|
|
453
|
+
imagePath: image ? image.path : null,
|
|
226
454
|
onProgress: (info) => {
|
|
227
455
|
console.log(formatProgress(info));
|
|
228
456
|
},
|
|
@@ -239,15 +467,108 @@ program
|
|
|
239
467
|
}
|
|
240
468
|
});
|
|
241
469
|
|
|
242
|
-
program.
|
|
470
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
471
|
+
console.error(`${colors.pink}${error.message}${colors.reset}`);
|
|
472
|
+
process.exitCode = 1;
|
|
473
|
+
});
|
|
243
474
|
|
|
244
475
|
/**
|
|
245
476
|
* The Interactive Chat Loop — Gemini-style TUI
|
|
246
477
|
*/
|
|
247
|
-
async function startInteractiveChat(initialMessage = null) {
|
|
478
|
+
async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
248
479
|
let lastResponseText = "";
|
|
249
|
-
|
|
250
|
-
|
|
480
|
+
let recentImageContextText = "";
|
|
481
|
+
const formatErrorMessage = (err) => err && err.message ? err.message : String(err || 'Unknown error');
|
|
482
|
+
const streamAssistantSentences = async (text, appendMessage, metadata = {}, streamMessage = null) => {
|
|
483
|
+
const sentences = splitResponseSentences(text);
|
|
484
|
+
if (typeof streamMessage === 'function') {
|
|
485
|
+
const stream = streamMessage(metadata);
|
|
486
|
+
for (let index = 0; index < sentences.length; index++) {
|
|
487
|
+
const prefix = index === 0 ? '' : ' ';
|
|
488
|
+
stream.appendChunk(`${prefix}${sentences[index]}`);
|
|
489
|
+
if (index < sentences.length - 1) {
|
|
490
|
+
await sleep(90);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
stream.finalize();
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
for (let index = 0; index < sentences.length; index++) {
|
|
498
|
+
appendMessage('assistant', sentences[index], index === 0 ? metadata : {});
|
|
499
|
+
if (index < sentences.length - 1) {
|
|
500
|
+
await sleep(90);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
const sendImageMessage = async ({ images, image, prompt, appendMessage, streamMessage, setThinking, appendCodeStep }) => {
|
|
505
|
+
const imageList = images || (image ? [image] : []);
|
|
506
|
+
const message = prompt || 'Analyze this image.';
|
|
507
|
+
const labels = imageList.map((_, index) => `[Image #${index + 1}]`).join(' ');
|
|
508
|
+
const displayMessage = labels && message.includes(labels)
|
|
509
|
+
? message
|
|
510
|
+
: `${message}\n${labels}`;
|
|
511
|
+
appendMessage('user', displayMessage);
|
|
512
|
+
if (appendCodeStep) {
|
|
513
|
+
appendCodeStep({
|
|
514
|
+
thought: imageList.length > 1
|
|
515
|
+
? `Analyzing ${imageList.length} attached images before answering.`
|
|
516
|
+
: 'Analyzing the attached image before answering.'
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
let seconds = 0;
|
|
521
|
+
setThinking(true, seconds);
|
|
522
|
+
const timer = setInterval(() => {
|
|
523
|
+
seconds++;
|
|
524
|
+
setThinking(true, seconds);
|
|
525
|
+
}, 1000);
|
|
526
|
+
|
|
527
|
+
try {
|
|
528
|
+
const result = await handleChat(message, imageList.map(item => item.dataUri), null);
|
|
529
|
+
clearInterval(timer);
|
|
530
|
+
setThinking(false);
|
|
531
|
+
const responseText = result.response || '';
|
|
532
|
+
lastResponseText = responseText;
|
|
533
|
+
recentImageContextText = [
|
|
534
|
+
`Recent image context: the user attached ${imageList.length} image(s) labelled ${labels || '[Image #1]'}.`,
|
|
535
|
+
'The terminal UI displays image attachments as labels only; it does not render thumbnails inside the chat.',
|
|
536
|
+
`Assistant response to those image(s): ${responseText}`
|
|
537
|
+
].join('\n');
|
|
538
|
+
await streamAssistantSentences(responseText, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
|
|
539
|
+
return responseText;
|
|
540
|
+
} catch (err) {
|
|
541
|
+
clearInterval(timer);
|
|
542
|
+
setThinking(false);
|
|
543
|
+
appendMessage('error', formatErrorMessage(err));
|
|
544
|
+
return '';
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const ui = await createChatUI({
|
|
549
|
+
onPasteImage: async () => {
|
|
550
|
+
try {
|
|
551
|
+
const image = loadClipboardImageAsDataUri();
|
|
552
|
+
return { label: image.path, image };
|
|
553
|
+
} catch (err) {
|
|
554
|
+
throw new Error(formatErrorMessage(err));
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
onSubmit: async (text, submitOptions = {}) => {
|
|
558
|
+
const { screen, appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode, appendCodeStep, updateWorkspace, askUser, attachImage, setInputText, setPendingPasteText, setFastMode, toggleFastMode, getFastMode } = ui;
|
|
559
|
+
if (submitOptions.images && submitOptions.images.length > 0) {
|
|
560
|
+
const images = submitOptions.images.map(item => item.image || item);
|
|
561
|
+
await sendImageMessage({
|
|
562
|
+
images,
|
|
563
|
+
prompt: text.trim() || 'Analyze this image.',
|
|
564
|
+
appendMessage,
|
|
565
|
+
streamMessage,
|
|
566
|
+
setThinking,
|
|
567
|
+
appendCodeStep
|
|
568
|
+
});
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
251
572
|
if (text.startsWith('/')) {
|
|
252
573
|
if (text.startsWith('/agent')) {
|
|
253
574
|
const args = text.split(' ');
|
|
@@ -339,14 +660,33 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
339
660
|
} else {
|
|
340
661
|
// Other slash commands
|
|
341
662
|
const fakeRl = { close: () => { } };
|
|
342
|
-
|
|
343
|
-
|
|
663
|
+
if (!text.startsWith('/image') && !text.startsWith('/paste')) {
|
|
664
|
+
appendMessage('user', text);
|
|
665
|
+
}
|
|
666
|
+
const slashResult = await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace, {
|
|
667
|
+
sendImageMessage,
|
|
668
|
+
formatErrorMessage,
|
|
669
|
+
attachImage,
|
|
670
|
+
setInputText,
|
|
671
|
+
setPendingPasteText,
|
|
672
|
+
setFastMode,
|
|
673
|
+
toggleFastMode,
|
|
674
|
+
getFastMode,
|
|
675
|
+
streamAssistantSentences,
|
|
676
|
+
streamMessage
|
|
677
|
+
});
|
|
678
|
+
if (slashResult && slashResult.lastResponseText) {
|
|
679
|
+
lastResponseText = slashResult.lastResponseText;
|
|
680
|
+
}
|
|
344
681
|
return;
|
|
345
682
|
}
|
|
346
683
|
}
|
|
347
684
|
appendMessage('user', text);
|
|
348
685
|
|
|
349
686
|
const transcript = await getChatTranscript();
|
|
687
|
+
const contextualHistory = recentImageContextText
|
|
688
|
+
? [...transcript, { sender: 'system', text: recentImageContextText, timestamp: new Date().toISOString() }]
|
|
689
|
+
: transcript;
|
|
350
690
|
if (setMode) setMode('Agent');
|
|
351
691
|
|
|
352
692
|
let seconds = 0;
|
|
@@ -360,45 +700,63 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
360
700
|
const config = require('./src/System/config_manager').readConfig();
|
|
361
701
|
const availableProviders = require('./src/System/config_manager').getAvailableProviders(config);
|
|
362
702
|
const preferredProvider = require('./src/CLI/code_agent')._helpers.selectSupportedCodeProvider(config, availableProviders);
|
|
703
|
+
let streamedFinalSummary = false;
|
|
363
704
|
|
|
364
705
|
const result = await executeCodeTask(text, {
|
|
365
706
|
cwd: process.cwd(),
|
|
366
707
|
requestApproval,
|
|
367
708
|
askUser,
|
|
368
709
|
provider: preferredProvider,
|
|
369
|
-
history:
|
|
710
|
+
history: contextualHistory,
|
|
370
711
|
onProgress: (info) => {
|
|
371
712
|
if (appendCodeStep) appendCodeStep(info);
|
|
713
|
+
},
|
|
714
|
+
onFinalSummary: async (info) => {
|
|
715
|
+
clearInterval(timer);
|
|
716
|
+
setThinking(false);
|
|
717
|
+
streamedFinalSummary = true;
|
|
718
|
+
await streamAssistantSentences(info.summary, appendMessage, { providerInfo: info.providerInfo }, streamMessage);
|
|
372
719
|
}
|
|
373
720
|
});
|
|
374
721
|
|
|
375
722
|
clearInterval(timer);
|
|
376
723
|
setThinking(false);
|
|
377
724
|
lastResponseText = result.summary;
|
|
378
|
-
|
|
725
|
+
if (!streamedFinalSummary) {
|
|
726
|
+
await streamAssistantSentences(result.summary, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
|
|
727
|
+
}
|
|
379
728
|
|
|
380
729
|
} catch (err) {
|
|
381
730
|
clearInterval(timer);
|
|
382
731
|
setThinking(false);
|
|
383
|
-
appendMessage('error', err
|
|
732
|
+
appendMessage('error', formatErrorMessage(err));
|
|
384
733
|
} finally {
|
|
385
|
-
if (setMode) setMode('
|
|
734
|
+
if (setMode) setMode('Agent');
|
|
386
735
|
}
|
|
387
736
|
},
|
|
388
737
|
onExit: () => {
|
|
389
|
-
|
|
390
|
-
// Explicitly restore terminal state and disable ALL mouse tracking modes
|
|
391
|
-
process.stdout.write('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l');
|
|
392
|
-
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
393
|
-
console.log(`\n${colors.pink}Goodbye! See you again soon!${colors.reset}\n`);
|
|
394
|
-
process.exit(0);
|
|
738
|
+
exitWithGoodbye(0);
|
|
395
739
|
}
|
|
396
740
|
});
|
|
397
741
|
|
|
742
|
+
// Handle initial image if passed via CLI option.
|
|
743
|
+
if (options.imagePath) {
|
|
744
|
+
const { appendMessage, streamMessage, setThinking, appendCodeStep } = ui;
|
|
745
|
+
const image = loadImageAsDataUri(options.imagePath);
|
|
746
|
+
const prompt = initialMessage || 'Analyze this image.';
|
|
747
|
+
await sendImageMessage({ images: [image], prompt, appendMessage, streamMessage, setThinking, appendCodeStep });
|
|
748
|
+
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
398
752
|
// Handle initial message if passed via CLI arg
|
|
399
753
|
if (initialMessage) {
|
|
754
|
+
const { appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode, appendCodeStep, updateWorkspace, askUser } = ui;
|
|
400
755
|
appendMessage('user', initialMessage);
|
|
401
756
|
const transcript = await getChatTranscript();
|
|
757
|
+
const contextualHistory = recentImageContextText
|
|
758
|
+
? [...transcript, { sender: 'system', text: recentImageContextText, timestamp: new Date().toISOString() }]
|
|
759
|
+
: transcript;
|
|
402
760
|
if (setMode) setMode('Agent');
|
|
403
761
|
|
|
404
762
|
let seconds = 0;
|
|
@@ -412,29 +770,38 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
412
770
|
const config = require('./src/System/config_manager').readConfig();
|
|
413
771
|
const availableProviders = require('./src/System/config_manager').getAvailableProviders(config);
|
|
414
772
|
const preferredProvider = require('./src/CLI/code_agent')._helpers.selectSupportedCodeProvider(config, availableProviders);
|
|
773
|
+
let streamedFinalSummary = false;
|
|
415
774
|
|
|
416
775
|
const result = await executeCodeTask(initialMessage, {
|
|
417
776
|
cwd: process.cwd(),
|
|
418
777
|
requestApproval,
|
|
419
778
|
askUser,
|
|
420
779
|
provider: preferredProvider,
|
|
421
|
-
history:
|
|
780
|
+
history: contextualHistory,
|
|
422
781
|
onProgress: (info) => {
|
|
423
782
|
if (appendCodeStep) appendCodeStep(info);
|
|
783
|
+
},
|
|
784
|
+
onFinalSummary: async (info) => {
|
|
785
|
+
clearInterval(timer);
|
|
786
|
+
setThinking(false);
|
|
787
|
+
streamedFinalSummary = true;
|
|
788
|
+
await streamAssistantSentences(info.summary, appendMessage, { providerInfo: info.providerInfo }, streamMessage);
|
|
424
789
|
}
|
|
425
790
|
});
|
|
426
791
|
|
|
427
792
|
clearInterval(timer);
|
|
428
793
|
setThinking(false);
|
|
429
794
|
lastResponseText = result.summary;
|
|
430
|
-
|
|
795
|
+
if (!streamedFinalSummary) {
|
|
796
|
+
await streamAssistantSentences(result.summary, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
|
|
797
|
+
}
|
|
431
798
|
|
|
432
799
|
} catch (err) {
|
|
433
800
|
clearInterval(timer);
|
|
434
801
|
setThinking(false);
|
|
435
|
-
appendMessage('error', err
|
|
802
|
+
appendMessage('error', formatErrorMessage(err));
|
|
436
803
|
} finally {
|
|
437
|
-
if (setMode) setMode('
|
|
804
|
+
if (setMode) setMode('Agent');
|
|
438
805
|
}
|
|
439
806
|
}
|
|
440
807
|
}
|
|
@@ -442,7 +809,7 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
442
809
|
/**
|
|
443
810
|
* Handles slash commands within the TUI context
|
|
444
811
|
*/
|
|
445
|
-
async function handleSlashCommandUI(input, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace) {
|
|
812
|
+
async function handleSlashCommandUI(input, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace, helpers = {}) {
|
|
446
813
|
const parts = input.split(' ');
|
|
447
814
|
const command = parts[0].toLowerCase();
|
|
448
815
|
const args = parts.slice(1);
|
|
@@ -452,9 +819,14 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
452
819
|
case '/?':
|
|
453
820
|
appendMessage('system', [
|
|
454
821
|
'Mint Slash Commands:',
|
|
822
|
+
' /image <path> [prompt] — Attach an image from your computer',
|
|
823
|
+
' /paste [prompt] — Attach an image from your clipboard',
|
|
824
|
+
' /fast [on|off] — Hide or show thinking/progress output',
|
|
825
|
+
' /learn <path> — Remember a .md/.txt file as a Mint skill',
|
|
455
826
|
' /code <task> — Force workspace Code Mode',
|
|
456
827
|
' /cd <path> — Change current working directory',
|
|
457
828
|
' /models [name] — List or switch Gemini models',
|
|
829
|
+
' /memory [cmd] — Manage long-term memory',
|
|
458
830
|
' /config — Show current configuration',
|
|
459
831
|
' /copy — Copy last response to clipboard',
|
|
460
832
|
' /clear — Clear conversation history',
|
|
@@ -463,6 +835,195 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
463
835
|
].join('\n'));
|
|
464
836
|
break;
|
|
465
837
|
|
|
838
|
+
case '/fast': {
|
|
839
|
+
if (!helpers.toggleFastMode || !helpers.setFastMode || !helpers.getFastMode) {
|
|
840
|
+
appendMessage('error', 'Fast mode is not available in this UI.');
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const option = (args[0] || '').toLowerCase();
|
|
845
|
+
let enabled;
|
|
846
|
+
if (option === 'on' || option === 'true' || option === '1') {
|
|
847
|
+
enabled = helpers.setFastMode(true);
|
|
848
|
+
} else if (option === 'off' || option === 'false' || option === '0') {
|
|
849
|
+
enabled = helpers.setFastMode(false);
|
|
850
|
+
} else if (option === 'status') {
|
|
851
|
+
enabled = helpers.getFastMode();
|
|
852
|
+
} else {
|
|
853
|
+
enabled = helpers.toggleFastMode();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
appendMessage('system', `Fast mode: ${enabled ? 'ON' : 'OFF'}`);
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
case '/learn': {
|
|
861
|
+
const filePath = input.slice(command.length).trim();
|
|
862
|
+
if (!filePath) {
|
|
863
|
+
appendMessage('system', 'Usage: /learn <path-to-skill.md>');
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
try {
|
|
868
|
+
const learned = learnSkillFile(filePath);
|
|
869
|
+
appendMessage('system', [
|
|
870
|
+
`✓ Learned skill: ${learned.name}`,
|
|
871
|
+
`Path: ${learned.source_path}`,
|
|
872
|
+
learned.stored_length < learned.content_length
|
|
873
|
+
? `Stored first ${learned.stored_length} of ${learned.content_length} characters.`
|
|
874
|
+
: `Stored ${learned.stored_length} characters.`
|
|
875
|
+
].join('\n'));
|
|
876
|
+
} catch (err) {
|
|
877
|
+
appendMessage('error', err && err.message ? err.message : String(err || 'Unknown error'));
|
|
878
|
+
}
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
case '/image': {
|
|
883
|
+
if (args.length === 0) {
|
|
884
|
+
appendMessage('system', 'Usage: /image <path> [prompt]');
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const imagePath = args[0];
|
|
889
|
+
const prompt = args.slice(1).join(' ').trim();
|
|
890
|
+
|
|
891
|
+
try {
|
|
892
|
+
const image = loadImageAsDataUri(imagePath);
|
|
893
|
+
if (helpers.attachImage) {
|
|
894
|
+
helpers.attachImage({ label: image.path, image });
|
|
895
|
+
if (prompt && helpers.setInputText) {
|
|
896
|
+
helpers.setInputText(prompt);
|
|
897
|
+
}
|
|
898
|
+
appendMessage('system', 'Attached image. Press Enter to send.');
|
|
899
|
+
} else {
|
|
900
|
+
appendMessage('error', 'Image attachment is not available in this UI.');
|
|
901
|
+
}
|
|
902
|
+
} catch (err) {
|
|
903
|
+
appendMessage('error', err && err.message ? err.message : String(err || 'Unknown error'));
|
|
904
|
+
}
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
case '/paste': {
|
|
909
|
+
try {
|
|
910
|
+
const image = loadClipboardImageAsDataUri();
|
|
911
|
+
if (helpers.attachImage) {
|
|
912
|
+
helpers.attachImage({ label: image.path, image });
|
|
913
|
+
const prompt = args.join(' ').trim();
|
|
914
|
+
if (prompt && helpers.setInputText) {
|
|
915
|
+
helpers.setInputText(prompt);
|
|
916
|
+
}
|
|
917
|
+
appendMessage('system', 'Attached clipboard image. Press Enter to send.');
|
|
918
|
+
} else {
|
|
919
|
+
appendMessage('error', 'Image attachment is not available in this UI.');
|
|
920
|
+
}
|
|
921
|
+
} catch (err) {
|
|
922
|
+
appendMessage('error', helpers.formatErrorMessage ? helpers.formatErrorMessage(err) : (err && err.message ? err.message : String(err || 'Unknown error')));
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
case '/memory': {
|
|
928
|
+
const subCommand = (args[0] || 'list').toLowerCase();
|
|
929
|
+
const query = args.slice(1).join(' ').trim();
|
|
930
|
+
|
|
931
|
+
if (subCommand === 'help') {
|
|
932
|
+
appendMessage('system', [
|
|
933
|
+
'Memory Commands:',
|
|
934
|
+
' /memory list [n] — Show recent remembered interactions',
|
|
935
|
+
' /memory search <query> — Search remembered interactions',
|
|
936
|
+
' /memory skills — Show learned skill files',
|
|
937
|
+
' /memory skills delete <id|path|name> — Delete a learned skill',
|
|
938
|
+
' /memory profile — Show remembered profile fields',
|
|
939
|
+
' /memory context [q] — Show context Mint injects into prompts',
|
|
940
|
+
' /memory delete <id> — Delete one remembered interaction',
|
|
941
|
+
' /memory export [path] — Export memory snapshot as JSON',
|
|
942
|
+
' /memory clear — Clear episodic interaction memories'
|
|
943
|
+
].join('\n'));
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (subCommand === 'profile') {
|
|
948
|
+
const profile = memoryStore.getAllProfile();
|
|
949
|
+
appendMessage('system', Object.keys(profile).length
|
|
950
|
+
? JSON.stringify(profile, null, 2)
|
|
951
|
+
: 'No profile memory stored yet.');
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (subCommand === 'skills') {
|
|
956
|
+
if ((args[1] || '').toLowerCase() === 'delete') {
|
|
957
|
+
const identifier = args.slice(2).join(' ').trim();
|
|
958
|
+
if (!identifier) {
|
|
959
|
+
appendMessage('system', 'Usage: /memory skills delete <id|path|name>');
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
const deleted = memoryStore.deleteLearnedSkill(identifier);
|
|
963
|
+
appendMessage('system', deleted > 0
|
|
964
|
+
? `Deleted learned skill: ${identifier}`
|
|
965
|
+
: `Learned skill not found: ${identifier}`);
|
|
966
|
+
break;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const skills = memoryStore.getLearnedSkills(20);
|
|
970
|
+
appendMessage('system', skills.length
|
|
971
|
+
? [
|
|
972
|
+
'Learned skills:',
|
|
973
|
+
...skills.map((skill) => `#${skill.id} ${skill.name}\n ${skill.source_path}`)
|
|
974
|
+
].join('\n')
|
|
975
|
+
: 'No learned skills stored yet.');
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (subCommand === 'context') {
|
|
980
|
+
const ctx = memoryStore.getUserContext(query);
|
|
981
|
+
appendMessage('system', ctx || 'No memory context stored yet.');
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (subCommand === 'search') {
|
|
986
|
+
if (!query) {
|
|
987
|
+
appendMessage('system', 'Usage: /memory search <query>');
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
const results = memoryStore.searchInteractions(query, 10);
|
|
991
|
+
appendMessage('system', formatMemoryInteractions(results, `Search results for "${query}"`));
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
if (subCommand === 'export') {
|
|
996
|
+
const exportPath = query
|
|
997
|
+
? path.resolve(process.cwd(), query)
|
|
998
|
+
: path.join(process.cwd(), `mint-memory-export-${Date.now()}.json`);
|
|
999
|
+
fs.writeFileSync(exportPath, JSON.stringify(memoryStore.exportMemorySnapshot(), null, 2), 'utf8');
|
|
1000
|
+
appendMessage('system', `Memory exported to: ${exportPath}`);
|
|
1001
|
+
break;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if (subCommand === 'delete') {
|
|
1005
|
+
const id = Number.parseInt(args[1] || '', 10);
|
|
1006
|
+
if (!Number.isFinite(id)) {
|
|
1007
|
+
appendMessage('system', 'Usage: /memory delete <id>');
|
|
1008
|
+
break;
|
|
1009
|
+
}
|
|
1010
|
+
const deleted = memoryStore.deleteInteractionMemory(id);
|
|
1011
|
+
appendMessage('system', deleted ? `Deleted memory #${id}.` : `Memory #${id} was not found.`);
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (subCommand === 'clear') {
|
|
1016
|
+
memoryStore.clearInteractionMemories();
|
|
1017
|
+
appendMessage('system', 'Cleared episodic interaction memories. Profile memory is unchanged.');
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const limit = Number.parseInt(args[0] || '10', 10);
|
|
1022
|
+
const interactions = memoryStore.getRecentInteractions(Number.isFinite(limit) ? limit : 10);
|
|
1023
|
+
appendMessage('system', formatMemoryInteractions(interactions, 'Recent remembered interactions'));
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
466
1027
|
case '/cd':
|
|
467
1028
|
if (args.length === 0) {
|
|
468
1029
|
appendMessage('system', `Current Directory: ${process.cwd()}`);
|
|
@@ -539,8 +1100,12 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
539
1100
|
await runChatRoutedTask(`/code ${args.join(' ')}`, {
|
|
540
1101
|
appendMessage,
|
|
541
1102
|
setThinking,
|
|
1103
|
+
requestApproval,
|
|
542
1104
|
appendCodeStep,
|
|
543
1105
|
setMode,
|
|
1106
|
+
streamAssistantSentences: helpers.streamAssistantSentences,
|
|
1107
|
+
streamMessage: helpers.streamMessage,
|
|
1108
|
+
askUser: () => Promise.resolve(''),
|
|
544
1109
|
history: await getChatTranscript()
|
|
545
1110
|
});
|
|
546
1111
|
break;
|
|
@@ -575,7 +1140,7 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
575
1140
|
|
|
576
1141
|
case '/exit':
|
|
577
1142
|
case '/quit':
|
|
578
|
-
|
|
1143
|
+
exitWithGoodbye(0);
|
|
579
1144
|
break;
|
|
580
1145
|
|
|
581
1146
|
default:
|