@pheem49/mint 1.5.0 → 1.5.2
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 +35 -1
- package/main.js +28 -14
- package/mint-cli-logic.js +3 -119
- package/mint-cli.js +201 -500
- 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 +40 -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 +15 -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 +40 -17
- package/src/AI_Brain/Gemini_API.js +147 -46
- package/src/AI_Brain/autonomous_brain.js +2 -1
- package/src/AI_Brain/memory_store.js +299 -3
- package/src/AI_Brain/proactive_engine.js +12 -2
- package/src/Automation_Layer/browser_automation.js +26 -24
- package/src/CLI/approval_handler.js +42 -0
- package/src/CLI/chat_router.js +18 -6
- package/src/CLI/chat_ui.js +583 -52
- package/src/CLI/cli_colors.js +32 -0
- package/src/CLI/cli_formatters.js +89 -0
- package/src/CLI/code_agent.js +369 -71
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/intent_detectors.js +181 -0
- package/src/CLI/interactive_chat.js +479 -0
- package/src/CLI/list_features.js +3 -0
- package/src/CLI/onboarding.js +72 -15
- package/src/CLI/repo_summarizer.js +282 -0
- package/src/CLI/semantic_code_search.js +312 -0
- package/src/CLI/skill_manager.js +41 -0
- package/src/CLI/slash_command_handler.js +418 -0
- package/src/CLI/symbol_indexer.js +231 -0
- package/src/CLI/updater.js +6 -4
- package/src/Channels/discord_bridge.js +11 -13
- package/src/Channels/line_bridge.js +10 -10
- package/src/Channels/slack_bridge.js +7 -12
- package/src/Channels/telegram_bridge.js +6 -14
- package/src/Channels/whatsapp_bridge.js +11 -9
- package/src/System/action_executor.js +59 -10
- package/src/System/chat_history_manager.js +20 -12
- package/src/System/config_manager.js +31 -1
- package/src/System/granular_automation.js +122 -53
- package/src/System/optional_require.js +23 -0
- package/src/System/proactive_loop.js +19 -3
- package/src/System/safety_manager.js +108 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +4 -2
- package/src/UI/live2d_manager.js +566 -0
- package/src/UI/renderer.js +339 -21
- package/src/UI/settings.css +655 -420
- package/src/UI/settings.html +478 -432
- package/src/UI/settings.js +10 -8
- package/src/UI/styles.css +516 -31
- 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/guide.html +0 -632
- package/docs/index.html +0 -133
- 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/action_executor_safety.test.js +0 -67
- 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/gmail.test.js +0 -135
- package/tests/gmail_auth.test.js +0 -129
- package/tests/google_calendar.test.js +0 -113
- package/tests/google_tts_urls.test.js +0 -24
- package/tests/memory_store.test.js +0 -185
- package/tests/notion.test.js +0 -121
- package/tests/provider_routing.test.js +0 -83
- package/tests/safety_manager.test.js +0 -40
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- package/tests/updater.test.js +0 -32
- package/tests/workspace_manager.test.js +0 -56
package/mint-cli.js
CHANGED
|
@@ -1,35 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
require('dotenv').config({ quiet: true });
|
|
3
|
+
|
|
3
4
|
// Suppress experimental SQLite warning
|
|
4
5
|
const originalEmit = process.emit;
|
|
5
6
|
process.emit = function (name, data, ...args) {
|
|
6
|
-
if (name === 'warning' && typeof data === 'object' &&
|
|
7
|
+
if (name === 'warning' && typeof data === 'object' &&
|
|
8
|
+
data.name === 'ExperimentalWarning' && data.message.includes('SQLite')) {
|
|
7
9
|
return false;
|
|
8
10
|
}
|
|
9
11
|
return originalEmit.apply(process, [name, data, ...args]);
|
|
10
12
|
};
|
|
13
|
+
|
|
11
14
|
const { Command } = require('commander');
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const {
|
|
17
|
-
const
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const {
|
|
22
|
-
const {
|
|
23
|
-
const {
|
|
24
|
-
const
|
|
25
|
-
const {
|
|
15
|
+
|
|
16
|
+
// ── CLI modules ──────────────────────────────────────────────────────────────
|
|
17
|
+
const { colors, exitWithGoodbye } = require('./src/CLI/cli_colors');
|
|
18
|
+
const { formatProgress } = require('./src/CLI/cli_formatters');
|
|
19
|
+
const { startInteractiveChat } = require('./src/CLI/interactive_chat');
|
|
20
|
+
const { requestCodeApproval } = require('./src/CLI/approval_handler');
|
|
21
|
+
const { learnSkillFile } = require('./src/CLI/skill_manager');
|
|
22
|
+
|
|
23
|
+
// ── Feature / system modules ────────────────────────────────────────────────
|
|
24
|
+
const { runOnboarding } = require('./src/CLI/onboarding');
|
|
25
|
+
const { startAgent } = require('./src/AI_Brain/headless_agent');
|
|
26
|
+
const { displayFeatures } = require('./src/CLI/list_features');
|
|
27
|
+
const { readConfig, writeConfig } = require('./src/System/config_manager');
|
|
28
|
+
const { executeCodeTask } = require('./src/CLI/code_agent');
|
|
29
|
+
const memoryStore = require('./src/AI_Brain/memory_store');
|
|
26
30
|
const { runUpdate, runStartupAutoUpdate, shouldRunAutoUpdate } = require('./src/CLI/updater');
|
|
27
|
-
const { runGmailAuth }
|
|
31
|
+
const { runGmailAuth } = require('./src/CLI/gmail_auth');
|
|
32
|
+
const { loadImageAsDataUri } = require('./src/CLI/image_input');
|
|
33
|
+
const { summarizeRepository, formatRepoSummary } = require('./src/CLI/repo_summarizer');
|
|
34
|
+
const { buildSymbolIndex, formatSymbolIndex } = require('./src/CLI/symbol_indexer');
|
|
35
|
+
const {
|
|
36
|
+
indexSemanticCode,
|
|
37
|
+
searchSemanticCode,
|
|
38
|
+
formatSemanticCodeIndex,
|
|
39
|
+
formatSemanticCodeSearch
|
|
40
|
+
} = require('./src/CLI/semantic_code_search');
|
|
41
|
+
|
|
42
|
+
const pkg = require('./package.json');
|
|
28
43
|
|
|
29
|
-
// Startup
|
|
30
|
-
const startupConfig
|
|
44
|
+
// ── Startup banner ───────────────────────────────────────────────────────────
|
|
45
|
+
const startupConfig = readConfig();
|
|
31
46
|
const startupProvider = startupConfig.aiProvider || 'gemini';
|
|
32
|
-
const startupModel
|
|
47
|
+
const startupModel = startupProvider === 'openai'
|
|
33
48
|
? (startupConfig.openaiModel || 'gpt-4o')
|
|
34
49
|
: startupProvider === 'anthropic'
|
|
35
50
|
? (startupConfig.anthropicModel || 'claude-3-5-sonnet-latest')
|
|
@@ -38,56 +53,17 @@ const startupModel = startupProvider === 'openai'
|
|
|
38
53
|
: startupProvider === 'ollama'
|
|
39
54
|
? (startupConfig.ollamaModel || 'llama3:latest')
|
|
40
55
|
: (startupConfig.geminiModel || 'gemini-2.5-flash');
|
|
41
|
-
const startupNow = new Date();
|
|
42
|
-
const startupTime = startupNow.toLocaleString('th-TH', {
|
|
43
|
-
day: '2-digit', month: '2-digit', year: 'numeric',
|
|
44
|
-
hour: '2-digit', minute: '2-digit', hour12: false
|
|
45
|
-
}).replace(',', '');
|
|
46
|
-
console.log(`\x1b[38;5;121m[Mint] v${pkg.version} | ${startupTime} | Active AI: ${startupProvider} • ${startupModel}\x1b[0m`);
|
|
47
|
-
|
|
48
|
-
// ANSI Colors
|
|
49
|
-
const colors = {
|
|
50
|
-
reset: "\x1b[0m",
|
|
51
|
-
bright: "\x1b[1m",
|
|
52
|
-
mint: "\x1b[38;5;121m",
|
|
53
|
-
pink: "\x1b[38;5;213m",
|
|
54
|
-
gray: "\x1b[90m",
|
|
55
|
-
cyan: "\x1b[36m",
|
|
56
|
-
yellow: "\x1b[33m"
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
function formatProgress(info) {
|
|
60
|
-
if (typeof info === 'string') return `${colors.gray}[Mint Code] ${info}${colors.reset}`;
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let icon = `${colors.mint}✓${colors.reset}`;
|
|
69
|
-
let label = action || phase;
|
|
70
|
-
let color = colors.reset;
|
|
71
|
-
|
|
72
|
-
switch (action) {
|
|
73
|
-
case 'thinking':
|
|
74
|
-
return `\n${colors.yellow}* ${colors.bright}Thinking${colors.reset}`;
|
|
75
|
-
case 'web_search': label = 'WebSearch'; break;
|
|
76
|
-
case 'list_files':
|
|
77
|
-
case 'find_path': label = 'Explored'; break;
|
|
78
|
-
case 'read_file': label = 'ReadFile'; break;
|
|
79
|
-
case 'search_code': label = 'SearchText'; break;
|
|
80
|
-
case 'apply_patch':
|
|
81
|
-
case 'write_file': label = 'Edited'; break;
|
|
82
|
-
case 'run_shell': label = 'Ran command'; break;
|
|
83
|
-
case 'json_repair': icon = '*'; label = 'Repairing JSON'; break;
|
|
84
|
-
case 'reviewer_start': label = 'Reviewing'; break;
|
|
85
|
-
}
|
|
57
|
+
const startupNow = new Date();
|
|
58
|
+
const startupTime = startupNow.toLocaleString('th-TH', {
|
|
59
|
+
day: '2-digit', month: '2-digit', year: 'numeric',
|
|
60
|
+
hour: '2-digit', minute: '2-digit', hour12: false
|
|
61
|
+
}).replace(',', '');
|
|
62
|
+
console.log(`${colors.mint}[Mint] v${pkg.version} | ${startupTime} | Active AI: ${startupProvider} • ${startupModel}${colors.reset}`);
|
|
86
63
|
|
|
87
|
-
|
|
88
|
-
return ` ${icon} ${colors.bright}${label}${colors.reset} ${color}${content}${colors.reset}`;
|
|
89
|
-
}
|
|
64
|
+
process.once('SIGINT', () => exitWithGoodbye(0));
|
|
90
65
|
|
|
66
|
+
// ── Commander program ────────────────────────────────────────────────────────
|
|
91
67
|
const program = new Command();
|
|
92
68
|
|
|
93
69
|
program
|
|
@@ -95,20 +71,11 @@ program
|
|
|
95
71
|
.description('Mint - Your Personal AI Assistant CLI')
|
|
96
72
|
.version(pkg.version);
|
|
97
73
|
|
|
74
|
+
// Auto-update hook
|
|
98
75
|
program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
99
|
-
if (actionCommand.name() === 'update' || process.env.MINT_SKIP_AUTO_UPDATE === '1')
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
76
|
+
if (actionCommand.name() === 'update' || process.env.MINT_SKIP_AUTO_UPDATE === '1') return;
|
|
103
77
|
const config = readConfig();
|
|
104
|
-
if (config.enableAutoUpdate === false)
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!shouldRunAutoUpdate(config)) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
78
|
+
if (config.enableAutoUpdate === false || !shouldRunAutoUpdate(config)) return;
|
|
112
79
|
console.log(`${colors.gray}[Mint Update] Checking for updates...${colors.reset}`);
|
|
113
80
|
const result = await runStartupAutoUpdate(config, writeConfig);
|
|
114
81
|
if (result.status === 'updated') {
|
|
@@ -118,16 +85,17 @@ program.hook('preAction', async (thisCommand, actionCommand) => {
|
|
|
118
85
|
}
|
|
119
86
|
});
|
|
120
87
|
|
|
121
|
-
//
|
|
88
|
+
// ── Commands ─────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
122
90
|
program
|
|
123
91
|
.command('chat', { isDefault: true })
|
|
124
92
|
.description('Start interactive chat session with Mint')
|
|
125
93
|
.argument('[message]', 'Initial message to send to Mint')
|
|
126
|
-
.
|
|
127
|
-
|
|
94
|
+
.option('-i, --image <path>', 'Attach an image file to the initial message')
|
|
95
|
+
.action(async (message, options) => {
|
|
96
|
+
await startInteractiveChat(message, { imagePath: options.image });
|
|
128
97
|
});
|
|
129
98
|
|
|
130
|
-
// Onboard Command
|
|
131
99
|
program
|
|
132
100
|
.command('onboard')
|
|
133
101
|
.description('Setup Mint for the first time')
|
|
@@ -136,7 +104,6 @@ program
|
|
|
136
104
|
await runOnboarding(options);
|
|
137
105
|
});
|
|
138
106
|
|
|
139
|
-
// Agent Command (Headless Daemon Mode)
|
|
140
107
|
program
|
|
141
108
|
.command('agent')
|
|
142
109
|
.description('Run Mint as a background agent (headless)')
|
|
@@ -150,15 +117,135 @@ program
|
|
|
150
117
|
await startAgent();
|
|
151
118
|
});
|
|
152
119
|
|
|
153
|
-
// List Command
|
|
154
120
|
program
|
|
155
121
|
.command('list')
|
|
156
122
|
.description('Show list of Mint features and commands')
|
|
157
|
-
.action(() =>
|
|
158
|
-
|
|
123
|
+
.action(() => displayFeatures());
|
|
124
|
+
|
|
125
|
+
program
|
|
126
|
+
.command('summarize')
|
|
127
|
+
.alias('summary')
|
|
128
|
+
.description('Summarize a repository structure, tooling, git state, and key files')
|
|
129
|
+
.argument('[path]', 'Repository path to summarize', process.cwd())
|
|
130
|
+
.option('--json', 'Print raw JSON summary')
|
|
131
|
+
.action((targetPath, options) => {
|
|
132
|
+
try {
|
|
133
|
+
const summary = summarizeRepository(targetPath);
|
|
134
|
+
if (options.json) { console.log(JSON.stringify(summary, null, 2)); return; }
|
|
135
|
+
console.log(`\n${formatRepoSummary(summary)}\n`);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`\n${colors.pink}Summarize failed:${colors.reset} ${error.message}\n`);
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
program
|
|
143
|
+
.command('symbols')
|
|
144
|
+
.alias('symbol-index')
|
|
145
|
+
.description('Build a source symbol index for the current repository')
|
|
146
|
+
.argument('[path]', 'Repository path to index', process.cwd())
|
|
147
|
+
.option('--json', 'Print raw JSON symbol index')
|
|
148
|
+
.option('--limit <count>', 'Limit formatted symbols shown', value => Number(value), 80)
|
|
149
|
+
.action((targetPath, options) => {
|
|
150
|
+
try {
|
|
151
|
+
const index = buildSymbolIndex(targetPath);
|
|
152
|
+
if (options.json) { console.log(JSON.stringify(index, null, 2)); return; }
|
|
153
|
+
console.log(`\n${formatSymbolIndex(index, { limit: options.limit })}\n`);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(`\n${colors.pink}Symbol index failed:${colors.reset} ${error.message}\n`);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const semanticCodeCommand = program
|
|
161
|
+
.command('semantic-code')
|
|
162
|
+
.alias('semantic')
|
|
163
|
+
.description('Index and search source code semantically with embeddings');
|
|
164
|
+
|
|
165
|
+
semanticCodeCommand
|
|
166
|
+
.command('index')
|
|
167
|
+
.description('Create embeddings for source code chunks in a repository')
|
|
168
|
+
.argument('[path]', 'Repository path to index', process.cwd())
|
|
169
|
+
.option('--json', 'Print raw JSON index metadata')
|
|
170
|
+
.action(async (targetPath, options) => {
|
|
171
|
+
try {
|
|
172
|
+
const index = await indexSemanticCode(targetPath, {
|
|
173
|
+
onProgress: (info) => {
|
|
174
|
+
if (info.current === 1 || info.current === info.total || info.current % 25 === 0) {
|
|
175
|
+
console.log(`${colors.gray}[Semantic Code] Embedded ${info.current}/${info.total}: ${info.file}${colors.reset}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
if (options.json) { console.log(JSON.stringify(index, null, 2)); return; }
|
|
180
|
+
console.log(`\n${formatSemanticCodeIndex(index)}\n`);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(`\n${colors.pink}Semantic code index failed:${colors.reset} ${error.message}\n`);
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
semanticCodeCommand
|
|
188
|
+
.command('search')
|
|
189
|
+
.description('Search an existing semantic code index')
|
|
190
|
+
.argument('<query...>', 'Natural language code search query')
|
|
191
|
+
.option('--path <path>', 'Repository path to search', process.cwd())
|
|
192
|
+
.option('--json', 'Print raw JSON search results')
|
|
193
|
+
.option('--top-k <count>', 'Number of results to return', value => Number(value), 5)
|
|
194
|
+
.action(async (query, options) => {
|
|
195
|
+
try {
|
|
196
|
+
const results = await searchSemanticCode(query.join(' '), options.path, { topK: options.topK });
|
|
197
|
+
if (options.json) { console.log(JSON.stringify(results, null, 2)); return; }
|
|
198
|
+
console.log(`\n${formatSemanticCodeSearch(results)}\n`);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(`\n${colors.pink}Semantic code search failed:${colors.reset} ${error.message}\n`);
|
|
201
|
+
process.exitCode = 1;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
program
|
|
206
|
+
.command('learn')
|
|
207
|
+
.description('Read a local markdown/text file and remember it as a Mint skill')
|
|
208
|
+
.argument('[filePath]', 'Path to a .md or .txt skill/instruction file')
|
|
209
|
+
.option('--delete <idOrPathOrName>', 'Delete a learned skill by id, path, or name')
|
|
210
|
+
.option('--list', 'List learned skills')
|
|
211
|
+
.action((filePath, options) => {
|
|
212
|
+
try {
|
|
213
|
+
if (options.list) {
|
|
214
|
+
const skills = memoryStore.getLearnedSkills(50);
|
|
215
|
+
if (skills.length === 0) { console.log(`\n${colors.gray}No learned skills stored.${colors.reset}\n`); return; }
|
|
216
|
+
console.log(`\n${colors.bright}Learned Skills:${colors.reset}`);
|
|
217
|
+
skills.forEach(skill => {
|
|
218
|
+
console.log(`${colors.mint}#${skill.id}${colors.reset} ${skill.name}`);
|
|
219
|
+
console.log(` ${colors.gray}${skill.source_path}${colors.reset}`);
|
|
220
|
+
});
|
|
221
|
+
console.log('');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (options.delete) {
|
|
225
|
+
const deleted = memoryStore.deleteLearnedSkill(options.delete);
|
|
226
|
+
if (deleted > 0) {
|
|
227
|
+
console.log(`\n${colors.mint}✓${colors.reset} Deleted learned skill: ${options.delete}\n`);
|
|
228
|
+
} else {
|
|
229
|
+
console.log(`\n${colors.pink}✗${colors.reset} Learned skill not found: ${options.delete}\n`);
|
|
230
|
+
process.exitCode = 1;
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!filePath) throw new Error('Usage: mint learn <path-to-skill.md>');
|
|
235
|
+
|
|
236
|
+
const learned = learnSkillFile(filePath);
|
|
237
|
+
console.log(`\n${colors.mint}✓${colors.reset} Learned skill: ${learned.name}`);
|
|
238
|
+
console.log(`${colors.gray}Path: ${learned.source_path}${colors.reset}`);
|
|
239
|
+
if (learned.stored_length < learned.content_length) {
|
|
240
|
+
console.log(`${colors.gray}Stored first ${learned.stored_length} of ${learned.content_length} characters.${colors.reset}`);
|
|
241
|
+
}
|
|
242
|
+
console.log('');
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(`\n${colors.pink}Learn failed:${colors.reset} ${error.message}\n`);
|
|
245
|
+
process.exitCode = 1;
|
|
246
|
+
}
|
|
159
247
|
});
|
|
160
248
|
|
|
161
|
-
// Task Command (Autonomous Background Task)
|
|
162
249
|
program
|
|
163
250
|
.command('task')
|
|
164
251
|
.description('Delegate a complex task to the background agent')
|
|
@@ -176,23 +263,18 @@ program
|
|
|
176
263
|
program
|
|
177
264
|
.command('update')
|
|
178
265
|
.description('Check for and install the latest Mint CLI version from npm')
|
|
179
|
-
.option('--check',
|
|
266
|
+
.option('--check', 'Only check whether an update is available')
|
|
180
267
|
.option('--dry-run', 'Show the npm update operation without installing')
|
|
181
268
|
.action(async (options) => {
|
|
182
269
|
console.log(`\n${colors.mint}${colors.bright}[Mint Update]${colors.reset} Checking npm for updates...`);
|
|
183
|
-
|
|
184
270
|
try {
|
|
185
271
|
const result = await runUpdate({
|
|
186
|
-
checkOnly: options.check
|
|
187
|
-
dryRun:
|
|
272
|
+
checkOnly: options.check === true,
|
|
273
|
+
dryRun: options.dryRun === true
|
|
188
274
|
});
|
|
189
|
-
|
|
190
275
|
const color = result.status === 'error' ? colors.pink : colors.mint;
|
|
191
276
|
console.log(`${color}${result.message}${colors.reset}\n`);
|
|
192
|
-
|
|
193
|
-
if (result.status === 'error') {
|
|
194
|
-
process.exitCode = 1;
|
|
195
|
-
}
|
|
277
|
+
if (result.status === 'error') process.exitCode = 1;
|
|
196
278
|
} catch (error) {
|
|
197
279
|
console.error(`${colors.pink}Update failed: ${error.message}${colors.reset}\n`);
|
|
198
280
|
process.exitCode = 1;
|
|
@@ -204,28 +286,21 @@ program
|
|
|
204
286
|
.description('Manage MCP (Model Context Protocol) servers')
|
|
205
287
|
.addCommand(new Command('add')
|
|
206
288
|
.description('Add a new MCP server')
|
|
207
|
-
.argument('<name>',
|
|
289
|
+
.argument('<name>', 'Server name')
|
|
208
290
|
.argument('<command>', 'Command to run (e.g. npx)')
|
|
209
291
|
.option('-a, --args <args...>', 'Command arguments')
|
|
210
|
-
.option('-e, --env <env...>',
|
|
292
|
+
.option('-e, --env <env...>', 'Environment variables (KEY=VALUE)')
|
|
211
293
|
.action((name, command, options) => {
|
|
212
|
-
const config
|
|
294
|
+
const config = readConfig();
|
|
213
295
|
const mcpServers = config.mcpServers || {};
|
|
214
|
-
|
|
215
|
-
const env = {};
|
|
296
|
+
const env = {};
|
|
216
297
|
if (options.env) {
|
|
217
298
|
options.env.forEach(kv => {
|
|
218
299
|
const [k, v] = kv.split('=');
|
|
219
300
|
if (k && v) env[k] = v;
|
|
220
301
|
});
|
|
221
302
|
}
|
|
222
|
-
|
|
223
|
-
mcpServers[name] = {
|
|
224
|
-
command,
|
|
225
|
-
args: options.args || [],
|
|
226
|
-
env
|
|
227
|
-
};
|
|
228
|
-
|
|
303
|
+
mcpServers[name] = { command, args: options.args || [], env };
|
|
229
304
|
config.mcpServers = mcpServers;
|
|
230
305
|
writeConfig(config);
|
|
231
306
|
console.log(`\n${colors.mint}✓${colors.reset} MCP server "${name}" added successfully.`);
|
|
@@ -248,7 +323,7 @@ program
|
|
|
248
323
|
.addCommand(new Command('list')
|
|
249
324
|
.description('List configured MCP servers')
|
|
250
325
|
.action(() => {
|
|
251
|
-
const config
|
|
326
|
+
const config = readConfig();
|
|
252
327
|
const servers = Object.keys(config.mcpServers || {});
|
|
253
328
|
if (servers.length === 0) {
|
|
254
329
|
console.log(`\n${colors.gray}No MCP servers configured.${colors.reset}`);
|
|
@@ -278,13 +353,13 @@ program
|
|
|
278
353
|
.addCommand(new Command('auth')
|
|
279
354
|
.description('Open Google OAuth login and save a Gmail refresh token')
|
|
280
355
|
.option('--port <port>', 'Local callback port, defaults to a random available port')
|
|
281
|
-
.option('--no-open',
|
|
356
|
+
.option('--no-open', 'Print the auth link without opening a browser')
|
|
282
357
|
.action(async (options) => {
|
|
283
358
|
try {
|
|
284
359
|
const result = await runGmailAuth({
|
|
285
|
-
port:
|
|
360
|
+
port: options.port ? Number(options.port) : 0,
|
|
286
361
|
openBrowser: options.open,
|
|
287
|
-
logger:
|
|
362
|
+
logger: console
|
|
288
363
|
});
|
|
289
364
|
console.log(`\n${colors.mint}✓${colors.reset} Gmail connected for ${result.userId}. Refresh token saved.`);
|
|
290
365
|
console.log(`${colors.gray}Scopes: ${result.scopes.join(', ')}${colors.reset}\n`);
|
|
@@ -299,16 +374,22 @@ program
|
|
|
299
374
|
.command('code')
|
|
300
375
|
.description('Run Mint in workspace-aware coding mode for the current project')
|
|
301
376
|
.argument('<task>', 'Coding task to execute in the current working directory')
|
|
302
|
-
.
|
|
377
|
+
.option('-i, --image <path>', 'Attach an image file as context for the coding task')
|
|
378
|
+
.action(async (task, options) => {
|
|
303
379
|
console.log(`\n${colors.mint}${colors.bright}[Mint Code]${colors.reset} Workspace: ${process.cwd()}`);
|
|
304
|
-
console.log(`${colors.gray}[Mint Code] Task: ${task}${colors.reset}\n`);
|
|
305
|
-
|
|
306
380
|
try {
|
|
381
|
+
let image = null;
|
|
382
|
+
if (options.image) {
|
|
383
|
+
image = loadImageAsDataUri(options.image);
|
|
384
|
+
console.log(`${colors.gray}[Mint Code] Image: ${image.path}${colors.reset}`);
|
|
385
|
+
}
|
|
386
|
+
console.log(`${colors.gray}[Mint Code] Task: ${task}${colors.reset}\n`);
|
|
387
|
+
|
|
307
388
|
const result = await executeCodeTask(task, {
|
|
308
|
-
cwd:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
389
|
+
cwd: process.cwd(),
|
|
390
|
+
imageDataUri: image ? image.dataUri : null,
|
|
391
|
+
imagePath: image ? image.path : null,
|
|
392
|
+
onProgress: (info) => console.log(formatProgress(info)),
|
|
312
393
|
requestApproval: requestCodeApproval
|
|
313
394
|
});
|
|
314
395
|
|
|
@@ -322,388 +403,8 @@ program
|
|
|
322
403
|
}
|
|
323
404
|
});
|
|
324
405
|
|
|
406
|
+
// ── Parse ────────────────────────────────────────────────────────────────────
|
|
325
407
|
program.parseAsync(process.argv).catch((error) => {
|
|
326
408
|
console.error(`${colors.pink}${error.message}${colors.reset}`);
|
|
327
409
|
process.exitCode = 1;
|
|
328
410
|
});
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* The Interactive Chat Loop — Gemini-style TUI
|
|
332
|
-
*/
|
|
333
|
-
async function startInteractiveChat(initialMessage = null) {
|
|
334
|
-
let lastResponseText = "";
|
|
335
|
-
const formatErrorMessage = (err) => err && err.message ? err.message : String(err || 'Unknown error');
|
|
336
|
-
|
|
337
|
-
const ui = await createChatUI({
|
|
338
|
-
onSubmit: async (text) => {
|
|
339
|
-
const { screen, appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode, appendCodeStep, updateWorkspace, askUser } = ui;
|
|
340
|
-
if (text.startsWith('/')) {
|
|
341
|
-
if (text.startsWith('/agent')) {
|
|
342
|
-
const args = text.split(' ');
|
|
343
|
-
if (args[1] === 'list') {
|
|
344
|
-
appendMessage('system', `Available Agents: ${agentOrchestrator.listAgents().join(', ')}`);
|
|
345
|
-
} else if (args[1]) {
|
|
346
|
-
const success = agentOrchestrator.setAgent(args[1]);
|
|
347
|
-
if (success) {
|
|
348
|
-
const agent = agentOrchestrator.getCurrentAgent();
|
|
349
|
-
appendMessage('system', `Switched to Agent: ${agent.icon} ${agent.name}`);
|
|
350
|
-
updateStatusModel(agent.name); // Pass name to status bar
|
|
351
|
-
resetChat(); // Reset to apply new system prompt
|
|
352
|
-
} else {
|
|
353
|
-
appendMessage('error', `Agent "${args[1]}" not found. Try /agent list`);
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
const agent = agentOrchestrator.getCurrentAgent();
|
|
357
|
-
appendMessage('system', `Current Agent: ${agent.icon} ${agent.name}\nUsage: /agent <type> or /agent list`);
|
|
358
|
-
}
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (text.startsWith('/stats')) {
|
|
363
|
-
appendMessage('system', '📊 Fetching system statistics...');
|
|
364
|
-
const stats = await systemMonitor.execute('stats');
|
|
365
|
-
appendMessage('system', stats);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (text.startsWith('/workspace')) {
|
|
370
|
-
const args = text.split(' ');
|
|
371
|
-
const subCmd = args[1];
|
|
372
|
-
|
|
373
|
-
if (subCmd === 'add') {
|
|
374
|
-
const name = args[2];
|
|
375
|
-
const wsPath = args[3] || '.';
|
|
376
|
-
const instructions = args.slice(4).join(' ');
|
|
377
|
-
if (!name) {
|
|
378
|
-
appendMessage('error', 'Usage: /workspace add <name> [path] [instructions]');
|
|
379
|
-
} else {
|
|
380
|
-
workspaceManager.addWorkspace(name, wsPath, instructions);
|
|
381
|
-
appendMessage('system', `Workspace "${name}" registered at ${path.resolve(wsPath)}`);
|
|
382
|
-
resetChat();
|
|
383
|
-
}
|
|
384
|
-
} else if (subCmd === 'list') {
|
|
385
|
-
const all = workspaceManager.listWorkspaces();
|
|
386
|
-
let listMsg = "Registered Workspaces:\n";
|
|
387
|
-
for (const n in all) listMsg += `- ${n}: ${all[n].path}\n`;
|
|
388
|
-
appendMessage('system', Object.keys(all).length ? listMsg : "No workspaces registered.");
|
|
389
|
-
} else if (subCmd === 'remove') {
|
|
390
|
-
const name = args[2];
|
|
391
|
-
if (workspaceManager.removeWorkspace(name)) {
|
|
392
|
-
appendMessage('system', `Removed workspace "${name}"`);
|
|
393
|
-
resetChat();
|
|
394
|
-
} else {
|
|
395
|
-
appendMessage('error', `Workspace "${name}" not found.`);
|
|
396
|
-
}
|
|
397
|
-
} else if (subCmd === 'use' || subCmd === 'switch') {
|
|
398
|
-
const name = args[2];
|
|
399
|
-
const all = workspaceManager.listWorkspaces();
|
|
400
|
-
if (all[name]) {
|
|
401
|
-
const newPath = all[name].path;
|
|
402
|
-
try {
|
|
403
|
-
process.chdir(newPath);
|
|
404
|
-
updateWorkspace(newPath);
|
|
405
|
-
appendMessage('system', `✓ Switched to workspace "${name}" at ${newPath}`);
|
|
406
|
-
resetChat();
|
|
407
|
-
} catch (e) {
|
|
408
|
-
appendMessage('error', `Failed to change directory: ${e.message}`);
|
|
409
|
-
}
|
|
410
|
-
} else {
|
|
411
|
-
appendMessage('error', `Workspace "${name}" not found. Try /workspace list`);
|
|
412
|
-
}
|
|
413
|
-
} else {
|
|
414
|
-
const ws = workspaceManager.getWorkspaceByPath(process.cwd());
|
|
415
|
-
appendMessage('system', ws ? `Current Workspace: ${ws.name}\nPath: ${ws.path}` : `Not currently in a registered workspace.\nActive Path: ${process.cwd()}\nUsage: /workspace <add|use|list|remove>`);
|
|
416
|
-
}
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (text.startsWith('/review')) {
|
|
421
|
-
if (!lastResponseText) {
|
|
422
|
-
appendMessage('error', 'Nothing to review yet. Get a response first.');
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
agentOrchestrator.setAgent('reviewer');
|
|
426
|
-
appendMessage('system', '⚖️ Requesting second-pass review from Mint Reviewer...');
|
|
427
|
-
text = `Please review this previous response and provide a critique:\n\n${lastResponseText}`;
|
|
428
|
-
} else {
|
|
429
|
-
// Other slash commands
|
|
430
|
-
const fakeRl = { close: () => { } };
|
|
431
|
-
appendMessage('user', text);
|
|
432
|
-
await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace);
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
appendMessage('user', text);
|
|
437
|
-
|
|
438
|
-
const transcript = await getChatTranscript();
|
|
439
|
-
if (setMode) setMode('Agent');
|
|
440
|
-
|
|
441
|
-
let seconds = 0;
|
|
442
|
-
setThinking(true, seconds);
|
|
443
|
-
const timer = setInterval(() => {
|
|
444
|
-
seconds++;
|
|
445
|
-
setThinking(true, seconds);
|
|
446
|
-
}, 1000);
|
|
447
|
-
|
|
448
|
-
try {
|
|
449
|
-
const config = require('./src/System/config_manager').readConfig();
|
|
450
|
-
const availableProviders = require('./src/System/config_manager').getAvailableProviders(config);
|
|
451
|
-
const preferredProvider = require('./src/CLI/code_agent')._helpers.selectSupportedCodeProvider(config, availableProviders);
|
|
452
|
-
|
|
453
|
-
const result = await executeCodeTask(text, {
|
|
454
|
-
cwd: process.cwd(),
|
|
455
|
-
requestApproval,
|
|
456
|
-
askUser,
|
|
457
|
-
provider: preferredProvider,
|
|
458
|
-
history: transcript,
|
|
459
|
-
onProgress: (info) => {
|
|
460
|
-
if (appendCodeStep) appendCodeStep(info);
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
clearInterval(timer);
|
|
465
|
-
setThinking(false);
|
|
466
|
-
lastResponseText = result.summary;
|
|
467
|
-
appendMessage('assistant', result.summary, { providerInfo: result.providerInfo });
|
|
468
|
-
|
|
469
|
-
} catch (err) {
|
|
470
|
-
clearInterval(timer);
|
|
471
|
-
setThinking(false);
|
|
472
|
-
appendMessage('error', formatErrorMessage(err));
|
|
473
|
-
} finally {
|
|
474
|
-
if (setMode) setMode('Chat');
|
|
475
|
-
}
|
|
476
|
-
},
|
|
477
|
-
onExit: () => {
|
|
478
|
-
// Explicitly restore terminal state and disable ALL mouse tracking modes
|
|
479
|
-
process.stdout.write('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l');
|
|
480
|
-
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
481
|
-
console.log(`\n${colors.pink}Goodbye! See you again soon!${colors.reset}\n`);
|
|
482
|
-
process.exit(0);
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
// Handle initial message if passed via CLI arg
|
|
487
|
-
if (initialMessage) {
|
|
488
|
-
const { appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode, appendCodeStep, updateWorkspace, askUser } = ui;
|
|
489
|
-
appendMessage('user', initialMessage);
|
|
490
|
-
const transcript = await getChatTranscript();
|
|
491
|
-
if (setMode) setMode('Agent');
|
|
492
|
-
|
|
493
|
-
let seconds = 0;
|
|
494
|
-
setThinking(true, seconds);
|
|
495
|
-
const timer = setInterval(() => {
|
|
496
|
-
seconds++;
|
|
497
|
-
setThinking(true, seconds);
|
|
498
|
-
}, 1000);
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
const config = require('./src/System/config_manager').readConfig();
|
|
502
|
-
const availableProviders = require('./src/System/config_manager').getAvailableProviders(config);
|
|
503
|
-
const preferredProvider = require('./src/CLI/code_agent')._helpers.selectSupportedCodeProvider(config, availableProviders);
|
|
504
|
-
|
|
505
|
-
const result = await executeCodeTask(initialMessage, {
|
|
506
|
-
cwd: process.cwd(),
|
|
507
|
-
requestApproval,
|
|
508
|
-
askUser,
|
|
509
|
-
provider: preferredProvider,
|
|
510
|
-
history: transcript,
|
|
511
|
-
onProgress: (info) => {
|
|
512
|
-
if (appendCodeStep) appendCodeStep(info);
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
clearInterval(timer);
|
|
517
|
-
setThinking(false);
|
|
518
|
-
lastResponseText = result.summary;
|
|
519
|
-
appendMessage('assistant', result.summary, { providerInfo: result.providerInfo });
|
|
520
|
-
|
|
521
|
-
} catch (err) {
|
|
522
|
-
clearInterval(timer);
|
|
523
|
-
setThinking(false);
|
|
524
|
-
appendMessage('error', formatErrorMessage(err));
|
|
525
|
-
} finally {
|
|
526
|
-
if (setMode) setMode('Chat');
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Handles slash commands within the TUI context
|
|
533
|
-
*/
|
|
534
|
-
async function handleSlashCommandUI(input, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace) {
|
|
535
|
-
const parts = input.split(' ');
|
|
536
|
-
const command = parts[0].toLowerCase();
|
|
537
|
-
const args = parts.slice(1);
|
|
538
|
-
|
|
539
|
-
switch (command) {
|
|
540
|
-
case '/help':
|
|
541
|
-
case '/?':
|
|
542
|
-
appendMessage('system', [
|
|
543
|
-
'Mint Slash Commands:',
|
|
544
|
-
' /code <task> — Force workspace Code Mode',
|
|
545
|
-
' /cd <path> — Change current working directory',
|
|
546
|
-
' /models [name] — List or switch Gemini models',
|
|
547
|
-
' /config — Show current configuration',
|
|
548
|
-
' /copy — Copy last response to clipboard',
|
|
549
|
-
' /clear — Clear conversation history',
|
|
550
|
-
' /reset — Reset conversation history',
|
|
551
|
-
' /exit — Exit Mint'
|
|
552
|
-
].join('\n'));
|
|
553
|
-
break;
|
|
554
|
-
|
|
555
|
-
case '/cd':
|
|
556
|
-
if (args.length === 0) {
|
|
557
|
-
appendMessage('system', `Current Directory: ${process.cwd()}`);
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
try {
|
|
561
|
-
const newPath = path.resolve(process.cwd(), args[0]);
|
|
562
|
-
if (fs.existsSync(newPath) && fs.lstatSync(newPath).isDirectory()) {
|
|
563
|
-
process.chdir(newPath);
|
|
564
|
-
if (updateWorkspace) updateWorkspace(newPath);
|
|
565
|
-
appendMessage('system', `✓ Directory changed to: ${newPath}`);
|
|
566
|
-
} else {
|
|
567
|
-
appendMessage('error', `Directory not found: ${newPath}`);
|
|
568
|
-
}
|
|
569
|
-
} catch (err) {
|
|
570
|
-
appendMessage('error', `Error: ${err.message}`);
|
|
571
|
-
}
|
|
572
|
-
break;
|
|
573
|
-
|
|
574
|
-
case '/model':
|
|
575
|
-
case '/models':
|
|
576
|
-
const config = readConfig();
|
|
577
|
-
if (args.length === 0) {
|
|
578
|
-
appendMessage('system', [
|
|
579
|
-
`Current Provider: ${config.aiProvider}`,
|
|
580
|
-
`Current Gemini Model: ${config.geminiModel}`,
|
|
581
|
-
'Available Providers/Presets:',
|
|
582
|
-
' - gemini-2.5-flash (Default Gemini)',
|
|
583
|
-
' - ollama (Local provider)',
|
|
584
|
-
' - anthropic (Claude)',
|
|
585
|
-
' - openai (GPT)',
|
|
586
|
-
' - huggingface (Inference API)',
|
|
587
|
-
' - local (LM Studio / OpenAI Compatible)',
|
|
588
|
-
'Usage: /models <name> to switch'
|
|
589
|
-
].join('\n'));
|
|
590
|
-
} else {
|
|
591
|
-
const { writeConfig } = require('./src/System/config_manager');
|
|
592
|
-
const newModel = args[0];
|
|
593
|
-
let newProvider = 'gemini';
|
|
594
|
-
|
|
595
|
-
if (newModel === 'ollama') {
|
|
596
|
-
newProvider = 'ollama';
|
|
597
|
-
} else if (newModel === 'anthropic') {
|
|
598
|
-
newProvider = 'anthropic';
|
|
599
|
-
} else if (newModel === 'openai') {
|
|
600
|
-
newProvider = 'openai';
|
|
601
|
-
} else if (newModel === 'huggingface') {
|
|
602
|
-
newProvider = 'huggingface';
|
|
603
|
-
} else if (newModel === 'local' || newModel === 'local_openai') {
|
|
604
|
-
newProvider = 'local_openai';
|
|
605
|
-
} else if (newModel.startsWith('gpt-')) {
|
|
606
|
-
newProvider = 'openai';
|
|
607
|
-
config.openaiModel = newModel;
|
|
608
|
-
} else if (newModel.startsWith('claude-')) {
|
|
609
|
-
newProvider = 'anthropic';
|
|
610
|
-
config.anthropicModel = newModel;
|
|
611
|
-
} else {
|
|
612
|
-
newProvider = 'gemini';
|
|
613
|
-
config.geminiModel = newModel;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
config.aiProvider = newProvider;
|
|
617
|
-
writeConfig(config);
|
|
618
|
-
appendMessage('system', `✅ Switched to: ${newProvider} ${newProvider === 'gemini' ? `(${newModel})` : ''}`);
|
|
619
|
-
if (updateStatusModel) updateStatusModel(newProvider === 'gemini' ? newModel : newProvider);
|
|
620
|
-
}
|
|
621
|
-
break;
|
|
622
|
-
|
|
623
|
-
case '/code':
|
|
624
|
-
if (args.length === 0) {
|
|
625
|
-
appendMessage('system', 'Usage: /code <task>');
|
|
626
|
-
break;
|
|
627
|
-
}
|
|
628
|
-
await runChatRoutedTask(`/code ${args.join(' ')}`, {
|
|
629
|
-
appendMessage,
|
|
630
|
-
setThinking,
|
|
631
|
-
requestApproval,
|
|
632
|
-
appendCodeStep,
|
|
633
|
-
setMode,
|
|
634
|
-
askUser: () => Promise.resolve(''),
|
|
635
|
-
history: await getChatTranscript()
|
|
636
|
-
});
|
|
637
|
-
break;
|
|
638
|
-
|
|
639
|
-
case '/config':
|
|
640
|
-
const currentCfg = readConfig();
|
|
641
|
-
appendMessage('system', [
|
|
642
|
-
'Current Configuration:',
|
|
643
|
-
` Version : v${pkg.version}`,
|
|
644
|
-
` Provider : ${currentCfg.aiProvider}`,
|
|
645
|
-
` Model : ${currentCfg.geminiModel}`,
|
|
646
|
-
` Ollama : ${currentCfg.ollamaModel}`,
|
|
647
|
-
` Voice : ${currentCfg.enableVoiceReply ? 'ON' : 'OFF'}`,
|
|
648
|
-
` Language : ${currentCfg.language}`,
|
|
649
|
-
` API Key : ${currentCfg.apiKey ? 'SET (****)' : 'NOT SET'}`
|
|
650
|
-
].join('\n'));
|
|
651
|
-
break;
|
|
652
|
-
|
|
653
|
-
case '/copy':
|
|
654
|
-
if (copyLastResponse && copyLastResponse()) {
|
|
655
|
-
appendMessage('system', '✓ Last response copied to clipboard.');
|
|
656
|
-
} else {
|
|
657
|
-
appendMessage('system', '✖ Nothing to copy, or xclip/xsel not installed.');
|
|
658
|
-
}
|
|
659
|
-
break;
|
|
660
|
-
|
|
661
|
-
case '/clear':
|
|
662
|
-
case '/reset':
|
|
663
|
-
resetChat();
|
|
664
|
-
appendMessage('system', 'Conversation history cleared.');
|
|
665
|
-
break;
|
|
666
|
-
|
|
667
|
-
case '/exit':
|
|
668
|
-
case '/quit':
|
|
669
|
-
process.exit(0);
|
|
670
|
-
break;
|
|
671
|
-
|
|
672
|
-
default:
|
|
673
|
-
appendMessage('system', `Unknown command: ${command}. Type /help for options.`);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
async function requestCodeApproval(request) {
|
|
678
|
-
const typeLabel = request.type === 'shell'
|
|
679
|
-
? 'Shell Command'
|
|
680
|
-
: request.type === 'patch'
|
|
681
|
-
? 'Patch Edit'
|
|
682
|
-
: 'File Write';
|
|
683
|
-
|
|
684
|
-
console.log(`\n${colors.yellow}${colors.bright}[Approval Required]${colors.reset} ${typeLabel}`);
|
|
685
|
-
if (request.label) {
|
|
686
|
-
console.log(`${colors.gray}${request.label}${colors.reset}`);
|
|
687
|
-
}
|
|
688
|
-
if (request.preview) {
|
|
689
|
-
console.log(`${colors.gray}${request.preview}${colors.reset}\n`);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
const rl = readline.createInterface({
|
|
693
|
-
input: process.stdin,
|
|
694
|
-
output: process.stdout
|
|
695
|
-
});
|
|
696
|
-
|
|
697
|
-
const answer = await new Promise((resolve) => {
|
|
698
|
-
rl.question('Approve this action? [y/N]: ', (value) => {
|
|
699
|
-
rl.close();
|
|
700
|
-
resolve((value || '').trim().toLowerCase());
|
|
701
|
-
});
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
const approved = answer === 'y' || answer === 'yes';
|
|
705
|
-
console.log(approved
|
|
706
|
-
? `${colors.mint}[Mint Code] Approved.${colors.reset}\n`
|
|
707
|
-
: `${colors.pink}[Mint Code] Denied.${colors.reset}\n`);
|
|
708
|
-
return approved;
|
|
709
|
-
}
|