@pheem49/mint 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,8 +22,9 @@
22
22
  ## 🌟 Highlights
23
23
 
24
24
  - **Dual-Mode AI**: Switch between a beautiful **Desktop GUI** and a professional **CLI**.
25
- - **Interactive Slash Commands**: Manage models and settings in the terminal with `/models`, `/config`, `/clear`, etc.
26
- - **Visual Slash Commands**: Intuitive command suggestions with a scrollable UI and descriptions.
25
+ - **Interactive Slash Commands**: Manage models and settings in the terminal with `/model`, `/config`, `/clear`, etc.
26
+ - **Smart TUI Experience**: Professional message framing, character-wrapped Thai text support, and mouse scroll wheel navigation.
27
+ - **System Information Action**: Retrieve OS, Kernel, and Architecture details via natural language.
27
28
  - **Dynamic UI Aesthetics**: Animated **Aura Glow** for the AI widget and **Glassmorphism** design.
28
29
  - **Minimize-to-Tray**: Keep Mint running in the background via the System Tray.
29
30
  - **Vision-Ready (Desktop)**: Capture, analyze, and translate any part of your screen in real-time.
@@ -131,7 +132,7 @@ When running in `agent` mode, Mint monitors your system in the background:
131
132
  ### Installation
132
133
  1. **Install via NPM (Recommended)**
133
134
  ```bash
134
- npm install -g @pheem49/mint
135
+ npm install -g @pheem49/mint@latest
135
136
  ```
136
137
 
137
138
  2. **Manual Installation (For Developers)**
package/mint-cli-logic.js CHANGED
@@ -35,6 +35,8 @@ async function executeAction(action) {
35
35
  return await pluginManager.executePlugin(action.pluginName, action.target);
36
36
  case 'system_automation':
37
37
  return await handleSystemAutomation(action.target);
38
+ case 'system_info':
39
+ return await SystemAutomation.getSystemInfo(action.target);
38
40
  default:
39
41
  return `Action ${action.type} is not yet fully supported in CLI.`;
40
42
  }
package/mint-cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- require('dotenv').config();
2
+ require('dotenv').config({ quiet: true });
3
3
  const { Command } = require('commander');
4
4
  const { handleChat, resetChat } = require('./src/AI_Brain/Gemini_API');
5
5
  const { runOnboarding } = require('./src/CLI/onboarding');
@@ -9,6 +9,16 @@ const { readConfig, writeConfig } = require('./src/System/config_manager');
9
9
  const readline = require('readline');
10
10
  const { createChatUI } = require('./src/CLI/chat_ui');
11
11
 
12
+ // Startup Info
13
+ const startupConfig = readConfig();
14
+ const startupModel = startupConfig.geminiModel || 'gemini-2.5-flash';
15
+ const startupNow = new Date();
16
+ const startupTime = startupNow.toLocaleString('th-TH', {
17
+ day: '2-digit', month: '2-digit', year: 'numeric',
18
+ hour: '2-digit', minute: '2-digit', hour12: false
19
+ }).replace(',', '');
20
+ console.log(`\x1b[38;5;121m[Mint] ${startupTime} | Active Model: ${startupModel}\x1b[0m`);
21
+
12
22
  // ANSI Colors
13
23
  const colors = {
14
24
  reset: "\x1b[0m",
@@ -23,64 +33,64 @@ const colors = {
23
33
  const program = new Command();
24
34
 
25
35
  program
26
- .name('mint-ai')
27
- .description('Mint - Your Personal AI Assistant CLI')
28
- .version('1.0.0');
36
+ .name('mint-ai')
37
+ .description('Mint - Your Personal AI Assistant CLI')
38
+ .version('1.0.0');
29
39
 
30
40
  // Chat Command (Interactive Mode)
31
41
  program
32
- .command('chat', { isDefault: true })
33
- .description('Start interactive chat session with Mint')
34
- .argument('[message]', 'Initial message to send to Mint')
35
- .action(async (message) => {
36
- await startInteractiveChat(message);
37
- });
42
+ .command('chat', { isDefault: true })
43
+ .description('Start interactive chat session with Mint')
44
+ .argument('[message]', 'Initial message to send to Mint')
45
+ .action(async (message) => {
46
+ await startInteractiveChat(message);
47
+ });
38
48
 
39
49
  // Onboard Command
40
50
  program
41
- .command('onboard')
42
- .description('Setup Mint for the first time')
43
- .option('--install-daemon', 'Automatically install systemd background agent')
44
- .action(async (options) => {
45
- await runOnboarding(options);
46
- });
51
+ .command('onboard')
52
+ .description('Setup Mint for the first time')
53
+ .option('--install-daemon', 'Automatically install systemd background agent')
54
+ .action(async (options) => {
55
+ await runOnboarding(options);
56
+ });
47
57
 
48
58
  // Agent Command (Headless Daemon Mode)
49
59
  program
50
- .command('agent')
51
- .description('Run Mint as a background agent (headless)')
52
- .argument('[initialTask]', 'Optional first task to perform immediately on startup')
53
- .action(async (initialTask) => {
54
- if (initialTask) {
55
- const taskManager = require('./src/System/task_manager');
56
- taskManager.addTask(initialTask);
57
- console.log(`\n${colors.mint}${colors.bright}[Mint-Agent] Starting with initial task:${colors.reset} "${initialTask}"`);
58
- }
59
- await startAgent();
60
- });
60
+ .command('agent')
61
+ .description('Run Mint as a background agent (headless)')
62
+ .argument('[initialTask]', 'Optional first task to perform immediately on startup')
63
+ .action(async (initialTask) => {
64
+ if (initialTask) {
65
+ const taskManager = require('./src/System/task_manager');
66
+ taskManager.addTask(initialTask);
67
+ console.log(`\n${colors.mint}${colors.bright}[Mint-Agent] Starting with initial task:${colors.reset} "${initialTask}"`);
68
+ }
69
+ await startAgent();
70
+ });
61
71
 
62
72
  // List Command
63
73
  program
64
- .command('list')
65
- .description('Show list of Mint features and commands')
66
- .action(() => {
67
- displayFeatures();
68
- });
74
+ .command('list')
75
+ .description('Show list of Mint features and commands')
76
+ .action(() => {
77
+ displayFeatures();
78
+ });
69
79
 
70
80
  // Task Command (Autonomous Background Task)
71
81
  program
72
- .command('task')
73
- .description('Delegate a complex task to the background agent')
74
- .argument('<description>', 'Description of the task for Mint to perform autonomously')
75
- .action(async (description) => {
76
- const taskManager = require('./src/System/task_manager');
77
- const task = taskManager.addTask(description);
78
- console.log(`\n${colors.mint}${colors.bright}Task Received!${colors.reset}`);
79
- console.log(`${colors.gray}Task ID: ${task.id}${colors.reset}`);
80
- console.log(`"${description}"`);
81
- console.log(`\n${colors.cyan}Mint Agent is starting to work on this in the background.${colors.reset}`);
82
- console.log(`${colors.gray}You will receive a notification when it's done.${colors.reset}\n`);
83
- });
82
+ .command('task')
83
+ .description('Delegate a complex task to the background agent')
84
+ .argument('<description>', 'Description of the task for Mint to perform autonomously')
85
+ .action(async (description) => {
86
+ const taskManager = require('./src/System/task_manager');
87
+ const task = taskManager.addTask(description);
88
+ console.log(`\n${colors.mint}${colors.bright}Task Received!${colors.reset}`);
89
+ console.log(`${colors.gray}Task ID: ${task.id}${colors.reset}`);
90
+ console.log(`"${description}"`);
91
+ console.log(`\n${colors.cyan}Mint Agent is starting to work on this in the background.${colors.reset}`);
92
+ console.log(`${colors.gray}You will receive a notification when it's done.${colors.reset}\n`);
93
+ });
84
94
 
85
95
  program.parse(process.argv);
86
96
 
@@ -92,7 +102,7 @@ async function startInteractiveChat(initialMessage = null) {
92
102
  onSubmit: async (text) => {
93
103
  if (text.startsWith('/')) {
94
104
  // Slash commands via fake rl-compatible object
95
- const fakeRl = { close: () => {} };
105
+ const fakeRl = { close: () => { } };
96
106
  appendMessage('user', text);
97
107
  await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse);
98
108
  return;
@@ -127,6 +137,9 @@ async function startInteractiveChat(initialMessage = null) {
127
137
  },
128
138
  onExit: () => {
129
139
  screen.destroy();
140
+ // Explicitly restore terminal state and disable ALL mouse tracking modes
141
+ process.stdout.write('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l');
142
+ process.stdout.write('\x1b[?25h'); // Show cursor
130
143
  console.log(`\n${colors.pink}Goodbye! See you again soon!${colors.reset}\n`);
131
144
  process.exit(0);
132
145
  }
@@ -173,6 +186,7 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
173
186
  ].join('\n'));
174
187
  break;
175
188
 
189
+ case '/model':
176
190
  case '/models':
177
191
  const config = readConfig();
178
192
  if (args.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pheem49/mint",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
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": {
@@ -6,7 +6,7 @@ const pluginManager = require('../Plugins/plugin_manager');
6
6
  let ai = null;
7
7
  let activeApiKey = '';
8
8
  const initialEnvKey = (process.env.GEMINI_API_KEY || '').trim();
9
- const DEFAULT_GEMINI_MODEL = 'gemini-2.0-flash'; // Optimized model
9
+ const DEFAULT_GEMINI_MODEL = 'gemini-2.5-flash'; // Optimized model
10
10
 
11
11
  function decodeUnicode(str) {
12
12
  if (!str) return '';
@@ -133,7 +133,7 @@ function createChat(history = []) {
133
133
 
134
134
  activeModel = resolveGeminiModel();
135
135
  if (activeModel && activeModel !== lastLoggedModel) {
136
- console.log(`[Gemini] Using model: ${activeModel}`);
136
+ // console.log(`[Gemini] Using model: ${activeModel}`);
137
137
  lastLoggedModel = activeModel;
138
138
  }
139
139
  chat = ai.chats.create({
@@ -8,7 +8,7 @@ const path = require('path');
8
8
 
9
9
  const os = require('os');
10
10
 
11
- const DEFAULT_GEMINI_MODEL = 'gemini-3.1-flash-lite-preview';
11
+ const DEFAULT_GEMINI_MODEL = 'gemini-2.5-flash';
12
12
 
13
13
  function expandHome(filePath) {
14
14
  if (filePath.startsWith('~/')) {
@@ -9,7 +9,7 @@ const { readConfig } = require('../System/config_manager');
9
9
  // ============================================================
10
10
 
11
11
  const ai = new GoogleGenAI({});
12
- const DEFAULT_GEMINI_MODEL = 'gemini-3.1-flash-lite-preview';
12
+ const DEFAULT_GEMINI_MODEL = 'gemini-2.5-flash';
13
13
  let lastLoggedModel = '';
14
14
 
15
15
  const PROACTIVE_SYSTEM_PROMPT = `You are a Smart Suggestion Engine built into a Desktop AI Agent called "Mint".
@@ -3,7 +3,7 @@ const { GoogleGenAI } = require('@google/genai');
3
3
  const { readConfig } = require('../System/config_manager');
4
4
 
5
5
  const ai = new GoogleGenAI({});
6
- const DEFAULT_GEMINI_MODEL = 'gemini-3.1-flash-lite-preview';
6
+ const DEFAULT_GEMINI_MODEL = 'gemini-2.5-flash';
7
7
  let lastLoggedModel = '';
8
8
 
9
9
  const BROWSER_SYSTEM_PROMPT = `You are an Autonomous Browser Agent. Your goal is to fulfill the user's web instruction by driving a headless browser.
@@ -35,12 +35,7 @@ function createChatUI({ onSubmit, onExit }) {
35
35
  smartCSR: true,
36
36
  fullUnicode: true,
37
37
  title: 'Mint CLI',
38
- cursor: {
39
- artificial: true,
40
- shape: 'line',
41
- blink: true,
42
- color: '#88e0b0'
43
- }
38
+ mouse: true
44
39
  });
45
40
 
46
41
  // ─── Banner ───────────────────────────────────────────────────────────────
@@ -75,7 +70,9 @@ function createChatUI({ onSubmit, onExit }) {
75
70
  scrollable: true,
76
71
  alwaysScroll: true,
77
72
  scrollbar: { ch: '│', style: { fg: '#334433' } },
78
- style: { bg: 'default', fg: '#ffffff' }
73
+ style: { bg: 'default', fg: '#ffffff' },
74
+ mouse: true,
75
+ scrollable: true
79
76
  });
80
77
 
81
78
  // ─── Divider above input ──────────────────────────────────────────────────
@@ -89,7 +86,7 @@ function createChatUI({ onSubmit, onExit }) {
89
86
  const hintBar = blessed.box({
90
87
  bottom: 6, left: 0, width: '100%', height: 1,
91
88
  tags: true,
92
- content: `{gray-fg} Shift+Tab to accept edits · /help for slash commands{/}`,
89
+ content: `{gray-fg} Shift+Drag to select text · Scroll to view history · /help for commands{/}`,
93
90
  style: { bg: 'default' }
94
91
  });
95
92
 
@@ -355,7 +352,7 @@ function createChatUI({ onSubmit, onExit }) {
355
352
  // Ctrl+C — double-press to exit
356
353
  let ctrlCPressed = false;
357
354
  let ctrlCTimer = null;
358
- const HINT_DEFAULT = `{gray-fg} Ctrl+Y copy last response · /help for commands{/}`;
355
+ const HINT_DEFAULT = `{gray-fg} Shift+Drag to select text · Ctrl+Y to copy · /help for commands{/}`;
359
356
 
360
357
  screen.key(['C-c'], () => {
361
358
  if (ctrlCPressed) {
@@ -431,24 +428,58 @@ function createChatUI({ onSubmit, onExit }) {
431
428
  * @param {string} timestamp - ISO string or Date object
432
429
  */
433
430
  function appendMessage(role, text, timestamp = null) {
434
- const lines = text.split('\n');
435
431
  const now = timestamp ? new Date(timestamp) : new Date();
436
432
  const timeStr = now.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit', hour12: false });
437
433
 
434
+ // Helper to wrap text manually since blessed.log doesn't support indenting wrapped lines
435
+ const wrapText = (str, width) => {
436
+ const lines = [];
437
+ const originalLines = str.split('\n');
438
+
439
+ for (let line of originalLines) {
440
+ if (line.length === 0) {
441
+ lines.push('');
442
+ continue;
443
+ }
444
+
445
+ let current = '';
446
+ for (let i = 0; i < line.length; i++) {
447
+ current += line[i];
448
+ // Simple wrap based on character count.
449
+ // Note: This is an approximation for Thai, but better than terminal auto-wrap.
450
+ if (current.length >= width) {
451
+ lines.push(current);
452
+ current = '';
453
+ }
454
+ }
455
+ if (current) lines.push(current);
456
+ }
457
+ return lines;
458
+ };
459
+
460
+ const maxLineWidth = Math.max(screen.width - 15, 40);
461
+
438
462
  if (role === 'user') {
439
- chatBox.log(`\n {bold}{#88e0b0-fg}>{/} {#ffffff-fg}${lines[0]}{/}`);
440
- lines.slice(1).forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
463
+ chatBox.log(`\n {bold}{#88e0b0-fg} You{/}`);
464
+ const lines = wrapText(text, maxLineWidth);
465
+ lines.forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
441
466
  chatBox.log(` {gray-fg}${timeStr}{/}`);
442
467
  } else if (role === 'assistant') {
443
- lastAssistantResponse = text; // track for Ctrl+Y
444
- chatBox.log(`\n {bold}{#d4a8ff-fg}Mint:{/} {#ffffff-fg}${lines[0]}{/}`);
445
- lines.slice(1).forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
446
- chatBox.log(` {gray-fg}${timeStr}{/}`);
447
- chatBox.log('');
468
+ lastAssistantResponse = text;
469
+ chatBox.log(`\n {bold}{#d4a8ff-fg}Mint{/}`);
470
+ const lines = wrapText(text, maxLineWidth);
471
+ lines.forEach(l => chatBox.log(` {#444444-fg}│{/} {#ffffff-fg}${l}{/}`));
472
+ chatBox.log(` {#444444-fg}┕${''.repeat(4)}{/} {gray-fg}${timeStr}{/}`);
448
473
  } else if (role === 'system') {
449
- chatBox.log(`\n {gray-fg}${text}{/}`);
474
+ const displayTag = text.startsWith('Action:') ? '{#88e0b0-fg}✦ Action:{/}' : '{#888888-fg}ℹ System:{/}';
475
+ const cleanText = text.replace(/^(Action:|System:)\s*/, '');
476
+ chatBox.log(`\n ${displayTag}`);
477
+ const lines = wrapText(cleanText, maxLineWidth - 2);
478
+ lines.forEach(l => chatBox.log(` {#ffffff-fg}${l}{/}`));
450
479
  } else if (role === 'error') {
451
- chatBox.log(`\n {red-fg}✖ ${text}{/}`);
480
+ chatBox.log(`\n {#ff5555-fg}✖ Error:{/}`);
481
+ const lines = wrapText(text, maxLineWidth - 2);
482
+ lines.forEach(l => chatBox.log(` {#ff5555-fg}${l}{/}`));
452
483
  }
453
484
  screen.render();
454
485
  }
@@ -28,7 +28,7 @@ class PluginManager {
28
28
  const plugin = require(pluginPath);
29
29
  if (this.validatePlugin(plugin)) {
30
30
  this.plugins.set(plugin.name, plugin);
31
- console.log(`[PluginManager] Loaded: ${plugin.name}`);
31
+ // console.log(`[PluginManager] Loaded: ${plugin.name}`);
32
32
  } else {
33
33
  console.warn(`[PluginManager] Invalid plugin format: ${file}`);
34
34
  }
@@ -67,7 +67,7 @@ class PluginManager {
67
67
  }
68
68
 
69
69
  try {
70
- console.log(`[PluginManager] Executing ${name} with instruction: "${instruction}"`);
70
+ // console.log(`[PluginManager] Executing ${name} with instruction: "${instruction}"`);
71
71
  return await plugin.execute(instruction);
72
72
  } catch (err) {
73
73
  console.error(`[PluginManager] Error executing plugin ${name}:`, err);
@@ -82,6 +82,34 @@ const SystemAutomation = {
82
82
  } catch (e) {
83
83
  throw new Error("xdotool not found. Cannot perform window management.");
84
84
  }
85
+ },
86
+
87
+ // System Information
88
+ async getSystemInfo(target = "") {
89
+ // If target is empty, return OS info
90
+ if (!target) {
91
+ try {
92
+ // Try lsb_release first
93
+ const osInfo = await execPromise('lsb_release -ds');
94
+ const kernel = await execPromise('uname -r');
95
+ const arch = await execPromise('uname -m');
96
+ return `Operating System: ${osInfo}\nKernel: ${kernel}\nArchitecture: ${arch}`;
97
+ } catch (e) {
98
+ try {
99
+ // Fallback to /etc/os-release
100
+ const osInfo = await execPromise('grep PRETTY_NAME /etc/os-release | cut -d\'"\' -f2');
101
+ const kernel = await execPromise('uname -r');
102
+ const arch = await execPromise('uname -m');
103
+ return `Operating System: ${osInfo}\nKernel: ${kernel}\nArchitecture: ${arch}`;
104
+ } catch (err) {
105
+ return "Could not retrieve OS information.";
106
+ }
107
+ }
108
+ }
109
+
110
+ // Handle weather or other info if target is provided
111
+ // For now, let's just return a placeholder or handle it if needed
112
+ return `System info for ${target} is not yet implemented.`;
85
113
  }
86
114
  };
87
115
 
@@ -6,7 +6,7 @@ const DEFAULT_CONFIG = {
6
6
  customBgEnd: '#1e1b4b',
7
7
  customPanelBg: '#1e293b',
8
8
  apiKey: '',
9
- geminiModel: 'gemini-3.1-flash-lite-preview',
9
+ geminiModel: 'gemini-2.5-flash',
10
10
  language: 'th-TH',
11
11
  proactiveInterval: 60,
12
12
  proactiveCooldown: 120,