@pheem49/mint 1.2.4 → 1.3.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/README.md +28 -0
- package/index.html +16 -0
- package/main.js +36 -83
- package/mint-cli-logic.js +19 -0
- package/mint-cli.js +117 -15
- package/package.json +8 -2
- package/src/AI_Brain/Gemini_API.js +175 -9
- package/src/AI_Brain/knowledge_base.js +199 -125
- package/src/Automation_Layer/file_operations.js +41 -19
- package/src/CLI/chat_router.js +166 -0
- package/src/CLI/chat_ui.js +239 -110
- package/src/CLI/code_agent.js +443 -0
- package/src/CLI/code_session_memory.js +62 -0
- package/src/CLI/list_features.js +1 -0
- package/src/Plugins/mcp_manager.js +95 -0
- package/src/Plugins/plugin_manager.js +2 -2
- package/src/System/config_manager.js +27 -7
- package/src/System/granular_automation.js +88 -0
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +98 -1
package/README.md
CHANGED
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
## 🌟 Highlights
|
|
23
23
|
|
|
24
24
|
- **Dual-Mode AI**: Switch between a beautiful **Desktop GUI** and a professional **CLI**.
|
|
25
|
+
- **Code Agent Mode**: Use `mint code "<task>"` to inspect, edit, and verify a project directly from the current terminal workspace.
|
|
26
|
+
- **Chat-First Workflow**: Regular Mint chat can now auto-route coding requests into workspace Code Mode, so the main chat acts as the control surface.
|
|
27
|
+
- **Visible Mode State**: The CLI status bar now shows whether Mint is currently in `Chat` or `Code` mode.
|
|
25
28
|
- **Interactive Slash Commands**: Manage models and settings in the terminal with `/model`, `/config`, `/clear`, etc.
|
|
26
29
|
- **Smart TUI Experience**: Professional message framing, character-wrapped Thai text support, and mouse scroll wheel navigation.
|
|
27
30
|
- **System Information Action**: Retrieve OS, Kernel, and Architecture details via natural language.
|
|
@@ -55,6 +58,7 @@ Mint CLI is built for speed and efficiency. Use the **`mint`** command from anyw
|
|
|
55
58
|
|
|
56
59
|
### 🚀 Professional Commands
|
|
57
60
|
- **`mint`** : Start interactive chat mode (Default).
|
|
61
|
+
- **`mint code "task"`** : Run Mint as a workspace-aware coding agent in the current project folder.
|
|
58
62
|
- **`mint agent`** : Run Mint as a headless background agent (Monitoring mode).
|
|
59
63
|
- **`mint agent "task"`** : **[NEW]** Start agent and execute an autonomous task immediately.
|
|
60
64
|
- **`mint task "task"`** : Delegate a multi-step task to an already running background agent.
|
|
@@ -66,6 +70,30 @@ Mint CLI is built for speed and efficiency. Use the **`mint`** command from anyw
|
|
|
66
70
|
### 🤖 Autonomous Agent (Task Delegate)
|
|
67
71
|
Mint isn't just a chatbot—it's an autonomous worker. Assign complex tasks that require multiple steps of reasoning.
|
|
68
72
|
|
|
73
|
+
### 👨💻 Code Agent Mode
|
|
74
|
+
Mint now includes a dedicated coding workflow as the first step toward a Claude-Code-like experience in the terminal.
|
|
75
|
+
|
|
76
|
+
**What it can do now:**
|
|
77
|
+
- Inspect the current workspace before editing
|
|
78
|
+
- Search code across the repo
|
|
79
|
+
- Read targeted file ranges
|
|
80
|
+
- Run non-destructive shell commands in the project with interactive approval
|
|
81
|
+
- Apply patch-based file edits with approval before changes are written
|
|
82
|
+
- Reuse lightweight session memory and git/test context per project
|
|
83
|
+
|
|
84
|
+
**Usage Example:**
|
|
85
|
+
```bash
|
|
86
|
+
mint code "fix the failing CLI command and verify it"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Chat-first Example:**
|
|
90
|
+
```bash
|
|
91
|
+
mint
|
|
92
|
+
> สำรวจโปรเจคนี้ให้หน่อย
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Mint will classify the request, switch the status bar to `Code`, and return to `Chat` mode after the coding flow finishes.
|
|
96
|
+
|
|
69
97
|
**Supported Autonomous Tools:**
|
|
70
98
|
- 🌐 **Web Automation**: Full Puppeteer-based browsing, info extraction, and research.
|
|
71
99
|
- 📁 **File System**: Create, Write, Delete, and Manage folders using `~/` path expansion.
|
package/index.html
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Mint AI</title>
|
|
7
|
+
<!-- We can add Google Fonts here -->
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="root"></div>
|
|
14
|
+
<script type="module" src="/ui/src/main.jsx"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
package/main.js
CHANGED
|
@@ -13,21 +13,22 @@ const { parseCommand } = require('./src/Command_Parser/parser');
|
|
|
13
13
|
const pluginManager = require('./src/Plugins/plugin_manager');
|
|
14
14
|
const { analyzeAndSuggest } = require('./src/AI_Brain/proactive_engine');
|
|
15
15
|
const { recordBehavior, getBehaviorSummary } = require('./src/AI_Brain/behavior_memory');
|
|
16
|
-
const { indexFile } = require('./src/AI_Brain/knowledge_base');
|
|
16
|
+
const { indexFile, indexFolder } = require('./src/AI_Brain/knowledge_base');
|
|
17
|
+
|
|
17
18
|
const SystemAutomation = require('./src/System/system_automation');
|
|
18
19
|
const systemEvents = require('./src/System/system_events');
|
|
19
20
|
const customWorkflows = require('./src/System/custom_workflows');
|
|
21
|
+
const mcpManager = require('./src/Plugins/mcp_manager');
|
|
22
|
+
const granularAutomation = require('./src/System/granular_automation');
|
|
20
23
|
const googleTTS = require('google-tts-api');
|
|
21
24
|
|
|
22
25
|
let mainWindow;
|
|
23
26
|
let settingsWindow = null;
|
|
24
27
|
let screenPickerWindow = null;
|
|
25
28
|
let spotlightWindow = null;
|
|
26
|
-
let floatingWindow = null;
|
|
27
29
|
let widgetWindow = null;
|
|
28
30
|
let proactiveGlowWindow = null;
|
|
29
31
|
let tray = null;
|
|
30
|
-
let floatingUnreadCount = 0;
|
|
31
32
|
|
|
32
33
|
// =====================
|
|
33
34
|
// Proactive Loop
|
|
@@ -192,7 +193,7 @@ function createWindow() {
|
|
|
192
193
|
});
|
|
193
194
|
|
|
194
195
|
mainWindow.on('focus', () => {
|
|
195
|
-
clearFloatingUnread();
|
|
196
|
+
// clearFloatingUnread(); // Disabled
|
|
196
197
|
});
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -291,41 +292,7 @@ function createSpotlightWindow() {
|
|
|
291
292
|
});
|
|
292
293
|
}
|
|
293
294
|
|
|
294
|
-
|
|
295
|
-
if (floatingWindow) return;
|
|
296
|
-
const display = screen.getPrimaryDisplay();
|
|
297
|
-
const { x, y, width, height } = display.workArea;
|
|
298
|
-
const size = 72;
|
|
299
|
-
const margin = 20;
|
|
300
|
-
const posX = x + width - size - margin;
|
|
301
|
-
const posY = y + height - size - margin - 40;
|
|
302
|
-
|
|
303
|
-
floatingWindow = new BrowserWindow({
|
|
304
|
-
width: size,
|
|
305
|
-
height: size,
|
|
306
|
-
x: posX,
|
|
307
|
-
y: posY,
|
|
308
|
-
frame: false,
|
|
309
|
-
transparent: true,
|
|
310
|
-
resizable: false,
|
|
311
|
-
alwaysOnTop: true,
|
|
312
|
-
skipTaskbar: true,
|
|
313
|
-
show: true,
|
|
314
|
-
webPreferences: {
|
|
315
|
-
preload: path.join(__dirname, 'src/UI/preload-floating.js'),
|
|
316
|
-
nodeIntegration: false,
|
|
317
|
-
contextIsolation: true
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
floatingWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
322
|
-
floatingWindow.setAlwaysOnTop(true, 'floating');
|
|
323
|
-
floatingWindow.loadFile('src/UI/floating.html');
|
|
324
|
-
|
|
325
|
-
floatingWindow.on('closed', () => {
|
|
326
|
-
floatingWindow = null;
|
|
327
|
-
});
|
|
328
|
-
}
|
|
295
|
+
// Floating window logic removed
|
|
329
296
|
|
|
330
297
|
function createWidgetWindow() {
|
|
331
298
|
if (widgetWindow) return;
|
|
@@ -363,28 +330,21 @@ function createWidgetWindow() {
|
|
|
363
330
|
});
|
|
364
331
|
}
|
|
365
332
|
|
|
366
|
-
|
|
367
|
-
if (floatingWindow && !floatingWindow.isDestroyed()) {
|
|
368
|
-
floatingWindow.webContents.send('floating-notify', floatingUnreadCount);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function clearFloatingUnread() {
|
|
373
|
-
if (floatingUnreadCount === 0) return;
|
|
374
|
-
floatingUnreadCount = 0;
|
|
375
|
-
updateFloatingUnread();
|
|
376
|
-
}
|
|
333
|
+
// Floating unread logic removed
|
|
377
334
|
|
|
378
335
|
app.whenReady().then(() => {
|
|
379
336
|
const config = readConfig();
|
|
380
337
|
createWindow();
|
|
381
338
|
createTray();
|
|
382
|
-
createFloatingWindow();
|
|
339
|
+
// createFloatingWindow(); // Removed
|
|
383
340
|
|
|
384
341
|
// Only show AI widget if enabled in settings
|
|
385
342
|
if (config.showDesktopWidget !== false) {
|
|
386
343
|
createWidgetWindow();
|
|
387
344
|
}
|
|
345
|
+
|
|
346
|
+
// Initialize MCP Servers
|
|
347
|
+
mcpManager.init().catch(err => console.error('[MCP] Init Error:', err));
|
|
388
348
|
|
|
389
349
|
// Start monitoring system events (battery, wifi, etc.)
|
|
390
350
|
systemEvents.startMonitoring();
|
|
@@ -395,7 +355,7 @@ app.whenReady().then(() => {
|
|
|
395
355
|
systemEvents.on('low-battery', (level) => {
|
|
396
356
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
397
357
|
mainWindow.webContents.send('proactive-notification', {
|
|
398
|
-
message: `⚠️
|
|
358
|
+
message: `⚠️ Battery is low (${level}%). Please plug in your charger. ✨`,
|
|
399
359
|
type: 'warning'
|
|
400
360
|
});
|
|
401
361
|
}
|
|
@@ -403,7 +363,7 @@ app.whenReady().then(() => {
|
|
|
403
363
|
|
|
404
364
|
systemEvents.on('connection-change', (isOnline) => {
|
|
405
365
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
406
|
-
const msg = isOnline ? '✅
|
|
366
|
+
const msg = isOnline ? '✅ Internet connection restored. ✨' : '❌ Internet connection lost.';
|
|
407
367
|
mainWindow.webContents.send('proactive-notification', { message: msg, type: 'info' });
|
|
408
368
|
}
|
|
409
369
|
});
|
|
@@ -438,6 +398,7 @@ app.on('window-all-closed', (e) => {
|
|
|
438
398
|
|
|
439
399
|
app.on('will-quit', () => {
|
|
440
400
|
globalShortcut.unregisterAll();
|
|
401
|
+
mcpManager.shutdown();
|
|
441
402
|
});
|
|
442
403
|
|
|
443
404
|
// =====================
|
|
@@ -473,7 +434,7 @@ ipcMain.on('close-window', (event) => {
|
|
|
473
434
|
});
|
|
474
435
|
|
|
475
436
|
ipcMain.on('minimize-window', (event) => {
|
|
476
|
-
if (mainWindow) mainWindow.
|
|
437
|
+
if (mainWindow) mainWindow.minimize();
|
|
477
438
|
});
|
|
478
439
|
|
|
479
440
|
ipcMain.on('quit-app', (event) => {
|
|
@@ -512,6 +473,7 @@ ipcMain.handle('get-settings', () => {
|
|
|
512
473
|
});
|
|
513
474
|
|
|
514
475
|
ipcMain.handle('save-settings', (event, config) => {
|
|
476
|
+
console.log('[Settings] Saving new config. MCP Servers count:', Object.keys(config.mcpServers || {}).length);
|
|
515
477
|
const result = writeConfig(config);
|
|
516
478
|
// Refresh API key if user updated it in settings
|
|
517
479
|
refreshApiKeyFromConfig();
|
|
@@ -597,33 +559,7 @@ ipcMain.on('spotlight-resize', (event, width, height) => {
|
|
|
597
559
|
}
|
|
598
560
|
});
|
|
599
561
|
|
|
600
|
-
//
|
|
601
|
-
// IPC — Floating Icon
|
|
602
|
-
// =====================
|
|
603
|
-
ipcMain.on('floating-click', () => {
|
|
604
|
-
if (mainWindow) {
|
|
605
|
-
mainWindow.show();
|
|
606
|
-
mainWindow.focus();
|
|
607
|
-
}
|
|
608
|
-
clearFloatingUnread();
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
ipcMain.on('ai-notify', () => {
|
|
612
|
-
floatingUnreadCount += 1;
|
|
613
|
-
updateFloatingUnread();
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
ipcMain.on('ai-notify-clear', () => {
|
|
617
|
-
clearFloatingUnread();
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
ipcMain.on('floating-drag-move', (_event, x, y) => {
|
|
621
|
-
if (!floatingWindow || floatingWindow.isDestroyed()) return;
|
|
622
|
-
const [width, height] = floatingWindow.getSize();
|
|
623
|
-
const posX = Math.round(x - width / 2);
|
|
624
|
-
const posY = Math.round(y - height / 2);
|
|
625
|
-
floatingWindow.setBounds({ x: posX, y: posY, width, height });
|
|
626
|
-
});
|
|
562
|
+
// Floating IPC removed
|
|
627
563
|
|
|
628
564
|
ipcMain.handle('open-external', (event, url) => {
|
|
629
565
|
shell.openExternal(url);
|
|
@@ -924,8 +860,11 @@ async function executeAction(action) {
|
|
|
924
860
|
createFolder(action.target);
|
|
925
861
|
break;
|
|
926
862
|
case 'open_file':
|
|
927
|
-
await openFile(action.target);
|
|
928
|
-
|
|
863
|
+
const fileRes = await openFile(action.target);
|
|
864
|
+
return fileRes || `Successfully opened file: ${action.target} ✅`;
|
|
865
|
+
case 'open_folder':
|
|
866
|
+
const folderRes = await openFile(action.target);
|
|
867
|
+
return folderRes || `Successfully opened folder: ${action.target} ✅`;
|
|
929
868
|
case 'delete_file':
|
|
930
869
|
await deleteFile(action.target);
|
|
931
870
|
break;
|
|
@@ -934,6 +873,20 @@ async function executeAction(action) {
|
|
|
934
873
|
break;
|
|
935
874
|
case 'learn_file':
|
|
936
875
|
return await indexFile(action.target);
|
|
876
|
+
case 'learn_folder':
|
|
877
|
+
const { indexFolder } = require('./src/AI_Brain/knowledge_base');
|
|
878
|
+
return await indexFolder(action.target);
|
|
879
|
+
case 'mcp_tool':
|
|
880
|
+
const mcpResult = await mcpManager.callTool(action.server, action.target, action.args);
|
|
881
|
+
return JSON.stringify(mcpResult.content);
|
|
882
|
+
case 'mouse_move':
|
|
883
|
+
return await granularAutomation.mouseMove(action.x, action.y);
|
|
884
|
+
case 'mouse_click':
|
|
885
|
+
return await granularAutomation.mouseClick(action.x, action.y, action.button || 1);
|
|
886
|
+
case 'type_text':
|
|
887
|
+
return await granularAutomation.typeText(action.target);
|
|
888
|
+
case 'key_tap':
|
|
889
|
+
return await granularAutomation.keyTap(action.target);
|
|
937
890
|
case 'plugin':
|
|
938
891
|
return await pluginManager.executePlugin(action.pluginName, action.target);
|
|
939
892
|
case 'system_automation':
|
package/mint-cli-logic.js
CHANGED
|
@@ -26,11 +26,30 @@ async function executeAction(action) {
|
|
|
26
26
|
case 'open_file':
|
|
27
27
|
await openFile(action.target);
|
|
28
28
|
return `Opening: ${action.target}`;
|
|
29
|
+
case 'open_folder':
|
|
30
|
+
await openFile(action.target);
|
|
31
|
+
return `Opening folder: ${action.target}`;
|
|
29
32
|
case 'delete_file':
|
|
30
33
|
await deleteFile(action.target);
|
|
31
34
|
return `Deleted: ${action.target}`;
|
|
32
35
|
case 'learn_file':
|
|
33
36
|
return await indexFile(action.target);
|
|
37
|
+
case 'mcp_tool':
|
|
38
|
+
const mcpManager = require('./src/Plugins/mcp_manager');
|
|
39
|
+
const mcpResult = await mcpManager.callTool(action.server, action.target, action.args);
|
|
40
|
+
return JSON.stringify(mcpResult.content);
|
|
41
|
+
case 'mouse_move':
|
|
42
|
+
const granularAutomation = require('./src/System/granular_automation');
|
|
43
|
+
return await granularAutomation.mouseMove(action.x, action.y);
|
|
44
|
+
case 'mouse_click':
|
|
45
|
+
const granularAutomationClick = require('./src/System/granular_automation');
|
|
46
|
+
return await granularAutomationClick.mouseClick(action.x, action.y, action.button || 1);
|
|
47
|
+
case 'type_text':
|
|
48
|
+
const granularAutomationType = require('./src/System/granular_automation');
|
|
49
|
+
return await granularAutomationType.typeText(action.target);
|
|
50
|
+
case 'key_tap':
|
|
51
|
+
const granularAutomationKey = require('./src/System/granular_automation');
|
|
52
|
+
return await granularAutomationKey.keyTap(action.target);
|
|
34
53
|
case 'plugin':
|
|
35
54
|
return await pluginManager.executePlugin(action.pluginName, action.target);
|
|
36
55
|
case 'system_automation':
|
package/mint-cli.js
CHANGED
|
@@ -7,6 +7,8 @@ const { runOnboarding } = require('./src/CLI/onboarding');
|
|
|
7
7
|
const { startAgent } = require('./src/AI_Brain/headless_agent');
|
|
8
8
|
const { displayFeatures } = require('./src/CLI/list_features');
|
|
9
9
|
const { readConfig, writeConfig } = require('./src/System/config_manager');
|
|
10
|
+
const { executeCodeTask } = require('./src/CLI/code_agent');
|
|
11
|
+
const { detectCodeIntent, runChatRoutedTask } = require('./src/CLI/chat_router');
|
|
10
12
|
const readline = require('readline');
|
|
11
13
|
const { createChatUI } = require('./src/CLI/chat_ui');
|
|
12
14
|
|
|
@@ -93,23 +95,64 @@ program
|
|
|
93
95
|
console.log(`${colors.gray}You will receive a notification when it's done.${colors.reset}\n`);
|
|
94
96
|
});
|
|
95
97
|
|
|
98
|
+
program
|
|
99
|
+
.command('code')
|
|
100
|
+
.description('Run Mint in workspace-aware coding mode for the current project')
|
|
101
|
+
.argument('<task>', 'Coding task to execute in the current working directory')
|
|
102
|
+
.action(async (task) => {
|
|
103
|
+
console.log(`\n${colors.mint}${colors.bright}[Mint Code]${colors.reset} Workspace: ${process.cwd()}`);
|
|
104
|
+
console.log(`${colors.gray}[Mint Code] Task: ${task}${colors.reset}\n`);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const result = await executeCodeTask(task, {
|
|
108
|
+
cwd: process.cwd(),
|
|
109
|
+
onProgress: (message) => {
|
|
110
|
+
console.log(`${colors.gray}[Mint Code] ${message}${colors.reset}`);
|
|
111
|
+
},
|
|
112
|
+
requestApproval: requestCodeApproval
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log(`\n${colors.mint}${colors.bright}Summary${colors.reset}`);
|
|
116
|
+
console.log(result.summary);
|
|
117
|
+
console.log(`\n${colors.cyan}Verification:${colors.reset} ${result.verification}`);
|
|
118
|
+
console.log(`${colors.gray}Completed in ${result.steps} step(s).${colors.reset}\n`);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`\n${colors.pink}[Mint Code Error]${colors.reset} ${error.message}\n`);
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
96
125
|
program.parse(process.argv);
|
|
97
126
|
|
|
98
127
|
/**
|
|
99
128
|
* The Interactive Chat Loop — Gemini-style TUI
|
|
100
129
|
*/
|
|
101
130
|
async function startInteractiveChat(initialMessage = null) {
|
|
102
|
-
const { screen, appendMessage, setThinking, updateStatusModel, copyLastResponse } = createChatUI({
|
|
131
|
+
const { screen, appendMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode } = createChatUI({
|
|
103
132
|
onSubmit: async (text) => {
|
|
104
133
|
if (text.startsWith('/')) {
|
|
105
134
|
// Slash commands via fake rl-compatible object
|
|
106
135
|
const fakeRl = { close: () => { } };
|
|
107
136
|
appendMessage('user', text);
|
|
108
|
-
await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse);
|
|
137
|
+
await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode);
|
|
109
138
|
return;
|
|
110
139
|
}
|
|
111
140
|
appendMessage('user', text);
|
|
112
141
|
|
|
142
|
+
const routeDecision = await detectCodeIntent(text, process.cwd());
|
|
143
|
+
if (routeDecision.route === 'code') {
|
|
144
|
+
appendMessage('system', `Router: entering Code Mode. ${routeDecision.reason}`);
|
|
145
|
+
await runChatRoutedTask(text, {
|
|
146
|
+
appendMessage,
|
|
147
|
+
setThinking,
|
|
148
|
+
requestApproval,
|
|
149
|
+
setMode
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
setMode('Chat');
|
|
155
|
+
|
|
113
156
|
// Start thinking timer
|
|
114
157
|
let seconds = 0;
|
|
115
158
|
setThinking(true, seconds);
|
|
@@ -149,18 +192,30 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
149
192
|
// Handle initial message if passed via CLI arg
|
|
150
193
|
if (initialMessage) {
|
|
151
194
|
appendMessage('user', initialMessage);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
195
|
+
const routeDecision = await detectCodeIntent(initialMessage, process.cwd());
|
|
196
|
+
if (routeDecision.route === 'code') {
|
|
197
|
+
appendMessage('system', `Router: entering Code Mode. ${routeDecision.reason}`);
|
|
198
|
+
await runChatRoutedTask(initialMessage, {
|
|
199
|
+
appendMessage,
|
|
200
|
+
setThinking,
|
|
201
|
+
requestApproval,
|
|
202
|
+
setMode
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
setMode('Chat');
|
|
206
|
+
let seconds = 0;
|
|
207
|
+
setThinking(true, seconds);
|
|
208
|
+
const timer = setInterval(() => { seconds++; setThinking(true, seconds); }, 1000);
|
|
209
|
+
try {
|
|
210
|
+
const response = await handleChat(initialMessage);
|
|
211
|
+
clearInterval(timer);
|
|
212
|
+
setThinking(false);
|
|
213
|
+
appendMessage('assistant', response.response, response.timestamp);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
clearInterval(timer);
|
|
216
|
+
setThinking(false);
|
|
217
|
+
appendMessage('error', err.message);
|
|
218
|
+
}
|
|
164
219
|
}
|
|
165
220
|
}
|
|
166
221
|
}
|
|
@@ -168,7 +223,7 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
168
223
|
/**
|
|
169
224
|
* Handles slash commands within the TUI context
|
|
170
225
|
*/
|
|
171
|
-
async function handleSlashCommandUI(input, appendMessage, updateStatusModel, copyLastResponse) {
|
|
226
|
+
async function handleSlashCommandUI(input, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode) {
|
|
172
227
|
const parts = input.split(' ');
|
|
173
228
|
const command = parts[0].toLowerCase();
|
|
174
229
|
const args = parts.slice(1);
|
|
@@ -178,6 +233,7 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
178
233
|
case '/?':
|
|
179
234
|
appendMessage('system', [
|
|
180
235
|
'Mint Slash Commands:',
|
|
236
|
+
' /code <task> — Force workspace Code Mode',
|
|
181
237
|
' /models [name] — List or switch Gemini models',
|
|
182
238
|
' /config — Show current configuration',
|
|
183
239
|
' /copy — Copy last response to clipboard',
|
|
@@ -215,6 +271,19 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
215
271
|
}
|
|
216
272
|
break;
|
|
217
273
|
|
|
274
|
+
case '/code':
|
|
275
|
+
if (args.length === 0) {
|
|
276
|
+
appendMessage('system', 'Usage: /code <task>');
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
await runChatRoutedTask(`/code ${args.join(' ')}`, {
|
|
280
|
+
appendMessage,
|
|
281
|
+
setThinking,
|
|
282
|
+
requestApproval,
|
|
283
|
+
setMode
|
|
284
|
+
});
|
|
285
|
+
break;
|
|
286
|
+
|
|
218
287
|
case '/config':
|
|
219
288
|
const currentCfg = readConfig();
|
|
220
289
|
appendMessage('system', [
|
|
@@ -253,3 +322,36 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
253
322
|
}
|
|
254
323
|
}
|
|
255
324
|
|
|
325
|
+
async function requestCodeApproval(request) {
|
|
326
|
+
const typeLabel = request.type === 'shell'
|
|
327
|
+
? 'Shell Command'
|
|
328
|
+
: request.type === 'patch'
|
|
329
|
+
? 'Patch Edit'
|
|
330
|
+
: 'File Write';
|
|
331
|
+
|
|
332
|
+
console.log(`\n${colors.yellow}${colors.bright}[Approval Required]${colors.reset} ${typeLabel}`);
|
|
333
|
+
if (request.label) {
|
|
334
|
+
console.log(`${colors.gray}${request.label}${colors.reset}`);
|
|
335
|
+
}
|
|
336
|
+
if (request.preview) {
|
|
337
|
+
console.log(`${colors.gray}${request.preview}${colors.reset}\n`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const rl = readline.createInterface({
|
|
341
|
+
input: process.stdin,
|
|
342
|
+
output: process.stdout
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const answer = await new Promise((resolve) => {
|
|
346
|
+
rl.question('Approve this action? [y/N]: ', (value) => {
|
|
347
|
+
rl.close();
|
|
348
|
+
resolve((value || '').trim().toLowerCase());
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const approved = answer === 'y' || answer === 'yes';
|
|
353
|
+
console.log(approved
|
|
354
|
+
? `${colors.mint}[Mint Code] Approved.${colors.reset}\n`
|
|
355
|
+
: `${colors.pink}[Mint Code] Denied.${colors.reset}\n`);
|
|
356
|
+
return approved;
|
|
357
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pheem49/mint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A powerful Electron-based AI desktop assistant powered by Google Gemini, featuring screen vision, web automation, and proactive suggestions.",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,24 +22,30 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@google/genai": "^1.44.0",
|
|
24
24
|
"@inkjs/ui": "^2.0.0",
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
25
26
|
"axios": "^1.13.6",
|
|
26
27
|
"blessed": "^0.1.81",
|
|
27
28
|
"cheerio": "^1.2.0",
|
|
28
29
|
"commander": "^14.0.3",
|
|
29
30
|
"dotenv": "^17.3.1",
|
|
31
|
+
"framer-motion": "^12.38.0",
|
|
30
32
|
"google-tts-api": "^2.0.2",
|
|
31
33
|
"ink": "^7.0.1",
|
|
32
34
|
"ink-text-input": "^6.0.0",
|
|
33
35
|
"inquirer": "^13.4.1",
|
|
36
|
+
"lucide-react": "^1.9.0",
|
|
34
37
|
"mammoth": "^1.12.0",
|
|
35
38
|
"pdf-parse": "^2.4.5",
|
|
36
39
|
"puppeteer": "^24.38.0",
|
|
37
40
|
"react": "^19.2.5",
|
|
41
|
+
"react-dom": "^19.2.5",
|
|
38
42
|
"xlsx": "^0.18.5"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
45
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
41
46
|
"electron": "^40.7.0",
|
|
42
|
-
"electron-builder": "^26.8.1"
|
|
47
|
+
"electron-builder": "^26.8.1",
|
|
48
|
+
"vite": "^8.0.10"
|
|
43
49
|
},
|
|
44
50
|
"build": {
|
|
45
51
|
"appId": "com.pheem49.mint",
|