@pheem49/mint 1.5.2 → 1.5.4
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 +23 -11
- package/README.md +148 -66
- package/assets/Agent_Mint.png +0 -0
- package/assets/Settings.png +0 -0
- package/install.ps1 +64 -0
- package/install.sh +54 -0
- package/main.js +12 -0
- package/package.json +5 -3
- package/preload.js +4 -0
- package/scripts/install_linux_desktop_entry.js +48 -0
- package/src/AI_Brain/Gemini_API.js +231 -498
- package/src/AI_Brain/autonomous_brain.js +46 -19
- package/src/AI_Brain/headless_agent.js +21 -2
- package/src/AI_Brain/provider_adapter.js +358 -0
- package/src/Automation_Layer/file_operations.js +17 -5
- package/src/CLI/approval_handler.js +5 -0
- package/src/CLI/chat_router.js +7 -0
- package/src/CLI/chat_ui.js +397 -76
- package/src/CLI/cli_colors.js +86 -3
- package/src/CLI/cli_formatters.js +6 -1
- package/src/CLI/code_agent.js +706 -273
- package/src/CLI/interactive_chat.js +311 -149
- package/src/CLI/slash_command_handler.js +2 -2
- package/src/CLI/updater.js +21 -1
- package/src/System/config_manager.js +5 -1
- package/src/System/ipc_handlers.js +95 -1
- package/src/System/picture_store.js +109 -0
- package/src/System/smart_context.js +227 -0
- package/src/System/task_manager.js +127 -0
- package/src/System/tool_registry.js +13 -0
- package/src/System/window_manager.js +16 -8
- package/src/UI/live2d_manager.js +42 -8
- package/src/UI/preload-spotlight.js +1 -0
- package/src/UI/renderer.js +837 -63
- package/src/UI/settings.css +160 -96
- package/src/UI/settings.html +9 -0
- package/src/UI/settings.js +35 -2
- package/src/UI/spotlight.js +13 -9
- package/src/UI/styles.css +1592 -165
- package/privacy.txt +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
const { colors, exitWithGoodbye } = require('./cli_colors');
|
|
4
5
|
const { splitResponseSentences } = require('./cli_formatters');
|
|
5
6
|
const {
|
|
@@ -28,9 +29,109 @@ const systemMonitor = require('../Plugins/system_monitor');
|
|
|
28
29
|
const workspaceManager = require('./workspace_manager');
|
|
29
30
|
const { executeCodeTask } = require('./code_agent');
|
|
30
31
|
const { resetChat } = require('../AI_Brain/Gemini_API');
|
|
32
|
+
const { saveChatImages } = require('../System/picture_store');
|
|
31
33
|
|
|
32
34
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
33
35
|
|
|
36
|
+
function createSessionStats() {
|
|
37
|
+
return {
|
|
38
|
+
sessionId: crypto.randomUUID(),
|
|
39
|
+
startedAt: Date.now(),
|
|
40
|
+
activeStartedAt: null,
|
|
41
|
+
agentActiveMs: 0,
|
|
42
|
+
toolCalls: { total: 0, success: 0, failed: 0 },
|
|
43
|
+
modelUsage: {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function addUsageRow(stats, row = {}) {
|
|
48
|
+
const provider = row.provider || 'unknown';
|
|
49
|
+
const model = row.model || 'unknown';
|
|
50
|
+
const key = `${provider}:${model}`;
|
|
51
|
+
if (!stats.modelUsage[key]) {
|
|
52
|
+
stats.modelUsage[key] = {
|
|
53
|
+
provider,
|
|
54
|
+
model,
|
|
55
|
+
requests: 0,
|
|
56
|
+
inputTokens: 0,
|
|
57
|
+
cacheReads: 0,
|
|
58
|
+
outputTokens: 0,
|
|
59
|
+
reasoningTokens: 0,
|
|
60
|
+
totalTokens: 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const target = stats.modelUsage[key];
|
|
65
|
+
target.requests += Number(row.requests) || 1;
|
|
66
|
+
target.inputTokens += Number(row.inputTokens) || 0;
|
|
67
|
+
target.cacheReads += Number(row.cacheReads) || 0;
|
|
68
|
+
target.outputTokens += Number(row.outputTokens) || 0;
|
|
69
|
+
target.reasoningTokens += Number(row.reasoningTokens) || 0;
|
|
70
|
+
target.totalTokens += Number(row.totalTokens) || 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeProviderUsage(providerInfo = {}) {
|
|
74
|
+
const usage = providerInfo.usage;
|
|
75
|
+
if (Array.isArray(usage)) return usage;
|
|
76
|
+
if (!usage || typeof usage !== 'object') {
|
|
77
|
+
return [{
|
|
78
|
+
provider: providerInfo.provider,
|
|
79
|
+
model: providerInfo.model,
|
|
80
|
+
requests: 1
|
|
81
|
+
}];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return [{
|
|
85
|
+
provider: providerInfo.provider,
|
|
86
|
+
model: providerInfo.model,
|
|
87
|
+
requests: 1,
|
|
88
|
+
inputTokens: usage.promptTokenCount || usage.prompt_tokens || usage.input_tokens,
|
|
89
|
+
cacheReads: usage.cachedContentTokenCount ||
|
|
90
|
+
(usage.prompt_tokens_details && usage.prompt_tokens_details.cached_tokens) ||
|
|
91
|
+
usage.cache_read_input_tokens,
|
|
92
|
+
outputTokens: usage.candidatesTokenCount || usage.completion_tokens || usage.output_tokens,
|
|
93
|
+
reasoningTokens: usage.thoughtsTokenCount ||
|
|
94
|
+
(usage.completion_tokens_details && usage.completion_tokens_details.reasoning_tokens),
|
|
95
|
+
totalTokens: usage.totalTokenCount || usage.total_tokens
|
|
96
|
+
}];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function recordProviderInfo(stats, providerInfo) {
|
|
100
|
+
if (!providerInfo) return;
|
|
101
|
+
for (const row of normalizeProviderUsage(providerInfo)) {
|
|
102
|
+
addUsageRow(stats, row);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function markAgentActive(stats, active) {
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
if (active && !stats.activeStartedAt) {
|
|
109
|
+
stats.activeStartedAt = now;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!active && stats.activeStartedAt) {
|
|
113
|
+
stats.agentActiveMs += now - stats.activeStartedAt;
|
|
114
|
+
stats.activeStartedAt = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildExitSummary(stats) {
|
|
119
|
+
const activeMs = stats.agentActiveMs + (stats.activeStartedAt ? Date.now() - stats.activeStartedAt : 0);
|
|
120
|
+
const total = stats.toolCalls.total;
|
|
121
|
+
return {
|
|
122
|
+
message: 'Agent powering down. Goodbye!',
|
|
123
|
+
sessionId: stats.sessionId,
|
|
124
|
+
toolCalls: {
|
|
125
|
+
...stats.toolCalls,
|
|
126
|
+
successRate: total ? (stats.toolCalls.success / total) * 100 : 0
|
|
127
|
+
},
|
|
128
|
+
wallMs: Date.now() - stats.startedAt,
|
|
129
|
+
agentActiveMs: activeMs,
|
|
130
|
+
modelUsage: Object.values(stats.modelUsage),
|
|
131
|
+
quotaHint: 'Use /models to view model quota information'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
34
135
|
// ---------------------------------------------------------------------------
|
|
35
136
|
// Internal helpers
|
|
36
137
|
// ---------------------------------------------------------------------------
|
|
@@ -143,12 +244,25 @@ async function sendSemanticCodeMessage({ rawArgs = '', appendMessage, streamMess
|
|
|
143
244
|
}
|
|
144
245
|
}
|
|
145
246
|
|
|
146
|
-
|
|
247
|
+
function hasAllImageLabels(message = '', imageCount = 0) {
|
|
248
|
+
const text = String(message || '');
|
|
249
|
+
for (let index = 1; index <= imageCount; index++) {
|
|
250
|
+
if (!text.includes(`[Image #${index}]`)) return false;
|
|
251
|
+
}
|
|
252
|
+
return imageCount > 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function formatImageDisplayMessage(message = '', labels = '', imageCount = 0) {
|
|
256
|
+
if (!labels) return message;
|
|
257
|
+
return hasAllImageLabels(message, imageCount) ? message : `${message}\n${labels}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function sendImageMessage({ images, image, prompt, appendMessage, streamMessage, setThinking, appendCodeStep, stats }) {
|
|
147
261
|
const formatErr = (err) => err && err.message ? err.message : String(err || 'Unknown error');
|
|
148
262
|
const imageList = images || (image ? [image] : []);
|
|
149
263
|
const message = prompt || 'Analyze this image.';
|
|
150
264
|
const labels = imageList.map((_, i) => `[Image #${i + 1}]`).join(' ');
|
|
151
|
-
const displayMessage = labels
|
|
265
|
+
const displayMessage = formatImageDisplayMessage(message, labels, imageList.length);
|
|
152
266
|
|
|
153
267
|
appendMessage('user', displayMessage);
|
|
154
268
|
if (appendCodeStep) {
|
|
@@ -160,17 +274,27 @@ async function sendImageMessage({ images, image, prompt, appendMessage, streamMe
|
|
|
160
274
|
}
|
|
161
275
|
|
|
162
276
|
const cancelTimer = startThinkingTimer(setThinking);
|
|
277
|
+
if (stats) markAgentActive(stats, true);
|
|
163
278
|
try {
|
|
164
|
-
const
|
|
279
|
+
const imageDataUris = imageList.map(item => item.dataUri);
|
|
280
|
+
const result = await handleChat(message, imageDataUris, null);
|
|
281
|
+
try {
|
|
282
|
+
saveChatImages(imageDataUris, { source: 'cli', message });
|
|
283
|
+
} catch (saveError) {
|
|
284
|
+
console.error('[Pictures] Failed to save CLI image:', saveError.message);
|
|
285
|
+
}
|
|
165
286
|
cancelTimer();
|
|
166
287
|
setThinking(false);
|
|
288
|
+
if (stats) markAgentActive(stats, false);
|
|
167
289
|
|
|
168
290
|
const responseText = result.response || '';
|
|
291
|
+
if (stats) recordProviderInfo(stats, result.providerInfo);
|
|
169
292
|
await streamAssistantSentences(responseText, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
|
|
170
293
|
return { responseText, labels, imageList };
|
|
171
294
|
} catch (err) {
|
|
172
295
|
cancelTimer();
|
|
173
296
|
setThinking(false);
|
|
297
|
+
if (stats) markAgentActive(stats, false);
|
|
174
298
|
appendMessage('error', formatErr(err));
|
|
175
299
|
return { responseText: '', labels, imageList };
|
|
176
300
|
}
|
|
@@ -189,6 +313,7 @@ async function runAgentTask(text, { appendMessage, streamMessage, setThinking, r
|
|
|
189
313
|
|
|
190
314
|
if (setMode) setMode('Agent');
|
|
191
315
|
const cancelTimer = startThinkingTimer(setThinking);
|
|
316
|
+
markAgentActive(sharedState.stats, true);
|
|
192
317
|
|
|
193
318
|
try {
|
|
194
319
|
const config = require('../System/config_manager').readConfig();
|
|
@@ -202,10 +327,19 @@ async function runAgentTask(text, { appendMessage, streamMessage, setThinking, r
|
|
|
202
327
|
askUser,
|
|
203
328
|
provider: preferredProvider,
|
|
204
329
|
history: contextualHistory,
|
|
205
|
-
onProgress: (info) => {
|
|
330
|
+
onProgress: (info) => {
|
|
331
|
+
if (info && info.phase === 'tool_call') {
|
|
332
|
+
sharedState.stats.toolCalls.total += 1;
|
|
333
|
+
if (info.status === 'success') sharedState.stats.toolCalls.success += 1;
|
|
334
|
+
else sharedState.stats.toolCalls.failed += 1;
|
|
335
|
+
}
|
|
336
|
+
if (appendCodeStep) appendCodeStep(info);
|
|
337
|
+
},
|
|
206
338
|
onFinalSummary: async (info) => {
|
|
207
339
|
cancelTimer();
|
|
208
340
|
setThinking(false);
|
|
341
|
+
markAgentActive(sharedState.stats, false);
|
|
342
|
+
recordProviderInfo(sharedState.stats, info.providerInfo);
|
|
209
343
|
streamedFinalSummary = true;
|
|
210
344
|
await streamAssistantSentences(info.summary, appendMessage, { providerInfo: info.providerInfo }, streamMessage);
|
|
211
345
|
}
|
|
@@ -213,13 +347,16 @@ async function runAgentTask(text, { appendMessage, streamMessage, setThinking, r
|
|
|
213
347
|
|
|
214
348
|
cancelTimer();
|
|
215
349
|
setThinking(false);
|
|
350
|
+
markAgentActive(sharedState.stats, false);
|
|
216
351
|
sharedState.lastResponseText = result.summary;
|
|
217
352
|
if (!streamedFinalSummary) {
|
|
353
|
+
recordProviderInfo(sharedState.stats, result.providerInfo);
|
|
218
354
|
await streamAssistantSentences(result.summary, appendMessage, { providerInfo: result.providerInfo }, streamMessage);
|
|
219
355
|
}
|
|
220
356
|
} catch (err) {
|
|
221
357
|
cancelTimer();
|
|
222
358
|
setThinking(false);
|
|
359
|
+
markAgentActive(sharedState.stats, false);
|
|
223
360
|
appendMessage('error', formatErr(err));
|
|
224
361
|
} finally {
|
|
225
362
|
if (setMode) setMode('Agent');
|
|
@@ -242,11 +379,14 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
|
242
379
|
// Shared mutable state between onSubmit closures
|
|
243
380
|
const sharedState = {
|
|
244
381
|
lastResponseText: '',
|
|
245
|
-
recentImageContextText: ''
|
|
382
|
+
recentImageContextText: '',
|
|
383
|
+
isBusy: false,
|
|
384
|
+
stats: createSessionStats()
|
|
246
385
|
};
|
|
247
386
|
|
|
248
387
|
// -----------------------------------------------------------------------
|
|
249
|
-
|
|
388
|
+
let ui;
|
|
389
|
+
ui = await createChatUI({
|
|
250
390
|
onPasteImage: async () => {
|
|
251
391
|
try {
|
|
252
392
|
const image = loadClipboardImageAsDataUri();
|
|
@@ -257,6 +397,12 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
|
257
397
|
},
|
|
258
398
|
|
|
259
399
|
onSubmit: async (text, submitOptions = {}) => {
|
|
400
|
+
if (sharedState.isBusy) {
|
|
401
|
+
ui.appendMessage('system', 'Mint is still working on the previous request. Please wait for it to finish before sending another command.');
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
sharedState.isBusy = true;
|
|
405
|
+
|
|
260
406
|
const {
|
|
261
407
|
appendMessage, streamMessage, setThinking, updateStatusModel,
|
|
262
408
|
copyLastResponse, requestApproval, setMode, appendCodeStep,
|
|
@@ -264,168 +410,177 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
|
264
410
|
setPendingPasteText, setFastMode, toggleFastMode, getFastMode
|
|
265
411
|
} = ui;
|
|
266
412
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
sharedState.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// ── Slash commands ──────────────────────────────────────────────
|
|
287
|
-
if (text.startsWith('/')) {
|
|
288
|
-
if (text.startsWith('/agent')) {
|
|
289
|
-
const aArgs = text.split(' ');
|
|
290
|
-
if (aArgs[1] === 'list') {
|
|
291
|
-
appendMessage('system', `Available Agents: ${agentOrchestrator.listAgents().join(', ')}`);
|
|
292
|
-
} else if (aArgs[1]) {
|
|
293
|
-
const success = agentOrchestrator.setAgent(aArgs[1]);
|
|
294
|
-
if (success) {
|
|
295
|
-
const agent = agentOrchestrator.getCurrentAgent();
|
|
296
|
-
appendMessage('system', `Switched to Agent: ${agent.icon} ${agent.name}`);
|
|
297
|
-
updateStatusModel(agent.name);
|
|
298
|
-
resetChat();
|
|
299
|
-
} else {
|
|
300
|
-
appendMessage('error', `Agent "${aArgs[1]}" not found. Try /agent list`);
|
|
301
|
-
}
|
|
302
|
-
} else {
|
|
303
|
-
const agent = agentOrchestrator.getCurrentAgent();
|
|
304
|
-
appendMessage('system', `Current Agent: ${agent.icon} ${agent.name}\nUsage: /agent <type> or /agent list`);
|
|
413
|
+
try {
|
|
414
|
+
// ── Image submission ────────────────────────────────────────
|
|
415
|
+
if (submitOptions.images && submitOptions.images.length > 0) {
|
|
416
|
+
const images = submitOptions.images.map(item => item.image || item);
|
|
417
|
+
const { responseText, labels, imageList } = await sendImageMessage({
|
|
418
|
+
images,
|
|
419
|
+
prompt: text.trim() || 'Analyze this image.',
|
|
420
|
+
appendMessage, streamMessage, setThinking, appendCodeStep,
|
|
421
|
+
stats: sharedState.stats
|
|
422
|
+
});
|
|
423
|
+
sharedState.lastResponseText = responseText;
|
|
424
|
+
if (responseText) {
|
|
425
|
+
sharedState.recentImageContextText = [
|
|
426
|
+
`Recent image context: the user attached ${imageList.length} image(s) labelled ${labels || '[Image #1]'}.`,
|
|
427
|
+
'The terminal UI displays image attachments as labels only; it does not render thumbnails inside the chat.',
|
|
428
|
+
`Assistant response to those image(s): ${responseText}`
|
|
429
|
+
].join('\n');
|
|
305
430
|
}
|
|
306
431
|
return;
|
|
307
432
|
}
|
|
308
433
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const wsPath = wArgs[3] || '.';
|
|
322
|
-
const instructions = wArgs.slice(4).join(' ');
|
|
323
|
-
if (!name) {
|
|
324
|
-
appendMessage('error', 'Usage: /workspace add <name> [path] [instructions]');
|
|
325
|
-
} else {
|
|
326
|
-
workspaceManager.addWorkspace(name, wsPath, instructions);
|
|
327
|
-
appendMessage('system', `Workspace "${name}" registered at ${require('path').resolve(wsPath)}`);
|
|
328
|
-
resetChat();
|
|
329
|
-
}
|
|
330
|
-
} else if (subCmd === 'list') {
|
|
331
|
-
const all = workspaceManager.listWorkspaces();
|
|
332
|
-
let listMsg = 'Registered Workspaces:\n';
|
|
333
|
-
for (const n in all) listMsg += `- ${n}: ${all[n].path}\n`;
|
|
334
|
-
appendMessage('system', Object.keys(all).length ? listMsg : 'No workspaces registered.');
|
|
335
|
-
} else if (subCmd === 'remove') {
|
|
336
|
-
const name = wArgs[2];
|
|
337
|
-
if (workspaceManager.removeWorkspace(name)) {
|
|
338
|
-
appendMessage('system', `Removed workspace "${name}"`);
|
|
339
|
-
resetChat();
|
|
340
|
-
} else {
|
|
341
|
-
appendMessage('error', `Workspace "${name}" not found.`);
|
|
342
|
-
}
|
|
343
|
-
} else if (subCmd === 'use' || subCmd === 'switch') {
|
|
344
|
-
const name = wArgs[2];
|
|
345
|
-
const all = workspaceManager.listWorkspaces();
|
|
346
|
-
if (all[name]) {
|
|
347
|
-
const newPath = all[name].path;
|
|
348
|
-
try {
|
|
349
|
-
process.chdir(newPath);
|
|
350
|
-
updateWorkspace(newPath);
|
|
351
|
-
appendMessage('system', `✓ Switched to workspace "${name}" at ${newPath}`);
|
|
434
|
+
// ── Slash commands ──────────────────────────────────────────
|
|
435
|
+
if (text.startsWith('/')) {
|
|
436
|
+
if (text.startsWith('/agent')) {
|
|
437
|
+
const aArgs = text.split(' ');
|
|
438
|
+
if (aArgs[1] === 'list') {
|
|
439
|
+
appendMessage('system', `Available Agents: ${agentOrchestrator.listAgents().join(', ')}`);
|
|
440
|
+
} else if (aArgs[1]) {
|
|
441
|
+
const success = agentOrchestrator.setAgent(aArgs[1]);
|
|
442
|
+
if (success) {
|
|
443
|
+
const agent = agentOrchestrator.getCurrentAgent();
|
|
444
|
+
appendMessage('system', `Switched to Agent: ${agent.icon} ${agent.name}`);
|
|
445
|
+
updateStatusModel(agent.name);
|
|
352
446
|
resetChat();
|
|
353
|
-
}
|
|
354
|
-
appendMessage('error', `
|
|
447
|
+
} else {
|
|
448
|
+
appendMessage('error', `Agent "${aArgs[1]}" not found. Try /agent list`);
|
|
355
449
|
}
|
|
356
450
|
} else {
|
|
357
|
-
|
|
451
|
+
const agent = agentOrchestrator.getCurrentAgent();
|
|
452
|
+
appendMessage('system', `Current Agent: ${agent.icon} ${agent.name}\nUsage: /agent <type> or /agent list`);
|
|
358
453
|
}
|
|
359
|
-
|
|
360
|
-
const ws = workspaceManager.getWorkspaceByPath(process.cwd());
|
|
361
|
-
appendMessage('system', ws
|
|
362
|
-
? `Current Workspace: ${ws.name}\nPath: ${ws.path}`
|
|
363
|
-
: `Not currently in a registered workspace.\nActive Path: ${process.cwd()}\nUsage: /workspace <add|use|list|remove>`);
|
|
454
|
+
return;
|
|
364
455
|
}
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
456
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
457
|
+
if (text.startsWith('/stats')) {
|
|
458
|
+
appendMessage('system', '📊 Fetching system statistics...');
|
|
459
|
+
const stats = await systemMonitor.execute('stats');
|
|
460
|
+
appendMessage('system', stats);
|
|
371
461
|
return;
|
|
372
462
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
463
|
+
|
|
464
|
+
if (text.startsWith('/workspace')) {
|
|
465
|
+
const wArgs = text.split(' ');
|
|
466
|
+
const subCmd = wArgs[1];
|
|
467
|
+
if (subCmd === 'add') {
|
|
468
|
+
const name = wArgs[2];
|
|
469
|
+
const wsPath = wArgs[3] || '.';
|
|
470
|
+
const instructions = wArgs.slice(4).join(' ');
|
|
471
|
+
if (!name) {
|
|
472
|
+
appendMessage('error', 'Usage: /workspace add <name> [path] [instructions]');
|
|
473
|
+
} else {
|
|
474
|
+
workspaceManager.addWorkspace(name, wsPath, instructions);
|
|
475
|
+
appendMessage('system', `Workspace "${name}" registered at ${require('path').resolve(wsPath)}`);
|
|
476
|
+
resetChat();
|
|
477
|
+
}
|
|
478
|
+
} else if (subCmd === 'list') {
|
|
479
|
+
const all = workspaceManager.listWorkspaces();
|
|
480
|
+
let listMsg = 'Registered Workspaces:\n';
|
|
481
|
+
for (const n in all) listMsg += `- ${n}: ${all[n].path}\n`;
|
|
482
|
+
appendMessage('system', Object.keys(all).length ? listMsg : 'No workspaces registered.');
|
|
483
|
+
} else if (subCmd === 'remove') {
|
|
484
|
+
const name = wArgs[2];
|
|
485
|
+
if (workspaceManager.removeWorkspace(name)) {
|
|
486
|
+
appendMessage('system', `Removed workspace "${name}"`);
|
|
487
|
+
resetChat();
|
|
488
|
+
} else {
|
|
489
|
+
appendMessage('error', `Workspace "${name}" not found.`);
|
|
490
|
+
}
|
|
491
|
+
} else if (subCmd === 'use' || subCmd === 'switch') {
|
|
492
|
+
const name = wArgs[2];
|
|
493
|
+
const all = workspaceManager.listWorkspaces();
|
|
494
|
+
if (all[name]) {
|
|
495
|
+
const newPath = all[name].path;
|
|
496
|
+
try {
|
|
497
|
+
process.chdir(newPath);
|
|
498
|
+
updateWorkspace(newPath);
|
|
499
|
+
appendMessage('system', `✓ Switched to workspace "${name}" at ${newPath}`);
|
|
500
|
+
resetChat();
|
|
501
|
+
} catch (e) {
|
|
502
|
+
appendMessage('error', `Failed to change directory: ${e.message}`);
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
appendMessage('error', `Workspace "${name}" not found. Try /workspace list`);
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
508
|
+
const ws = workspaceManager.getWorkspaceByPath(process.cwd());
|
|
509
|
+
appendMessage('system', ws
|
|
510
|
+
? `Current Workspace: ${ws.name}\nPath: ${ws.path}`
|
|
511
|
+
: `Not currently in a registered workspace.\nActive Path: ${process.cwd()}\nUsage: /workspace <add|use|list|remove>`);
|
|
512
|
+
}
|
|
513
|
+
return;
|
|
379
514
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
setFastMode, toggleFastMode, getFastMode,
|
|
386
|
-
sendRepoSummaryMessage, sendSymbolIndexMessage,
|
|
387
|
-
sendSemanticCodeMessage, streamAssistantSentences,
|
|
388
|
-
streamMessage
|
|
515
|
+
|
|
516
|
+
if (text.startsWith('/review')) {
|
|
517
|
+
if (!sharedState.lastResponseText) {
|
|
518
|
+
appendMessage('error', 'Nothing to review yet. Get a response first.');
|
|
519
|
+
return;
|
|
389
520
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
521
|
+
agentOrchestrator.setAgent('reviewer');
|
|
522
|
+
appendMessage('system', '⚖️ Requesting second-pass review from Mint Reviewer...');
|
|
523
|
+
text = `Please review this previous response and provide a critique:\n\n${sharedState.lastResponseText}`;
|
|
524
|
+
} else {
|
|
525
|
+
if (!text.startsWith('/image') && !text.startsWith('/paste')) {
|
|
526
|
+
appendMessage('user', text);
|
|
527
|
+
}
|
|
528
|
+
const slashResult = await handleSlashCommandUI(
|
|
529
|
+
text, appendMessage, updateStatusModel, copyLastResponse,
|
|
530
|
+
setThinking, requestApproval, setMode, appendCodeStep, updateWorkspace, {
|
|
531
|
+
sendImageMessage: (args) => sendImageMessage({ ...args, stats: sharedState.stats }),
|
|
532
|
+
formatErrorMessage: formatErr,
|
|
533
|
+
attachImage, setInputText, setPendingPasteText,
|
|
534
|
+
setFastMode, toggleFastMode, getFastMode,
|
|
535
|
+
sendRepoSummaryMessage, sendSymbolIndexMessage,
|
|
536
|
+
sendSemanticCodeMessage, streamAssistantSentences,
|
|
537
|
+
streamMessage
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
if (slashResult && slashResult.lastResponseText) {
|
|
541
|
+
sharedState.lastResponseText = slashResult.lastResponseText;
|
|
542
|
+
}
|
|
543
|
+
return;
|
|
393
544
|
}
|
|
394
|
-
return;
|
|
395
545
|
}
|
|
396
|
-
}
|
|
397
546
|
|
|
398
|
-
|
|
547
|
+
appendMessage('user', text);
|
|
399
548
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
549
|
+
// ── Local tool shortcuts (natural language) ─────────────────
|
|
550
|
+
if (isRepoSummaryRequest(text)) {
|
|
551
|
+
const r = await sendRepoSummaryMessage({ appendMessage, streamMessage, setThinking });
|
|
552
|
+
sharedState.lastResponseText = r;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (isSymbolIndexRequest(text)) {
|
|
556
|
+
const r = await sendSymbolIndexMessage({ appendMessage, streamMessage, setThinking });
|
|
557
|
+
sharedState.lastResponseText = r;
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (isSemanticCodeSearchRequest(text)) {
|
|
561
|
+
const query = extractSemanticCodeQuery(text);
|
|
562
|
+
const r = await sendSemanticCodeMessage({
|
|
563
|
+
rawArgs: `search ${query}`,
|
|
564
|
+
appendMessage, streamMessage, setThinking, appendCodeStep
|
|
565
|
+
});
|
|
566
|
+
sharedState.lastResponseText = r;
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
420
569
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
570
|
+
// ── Default to guarded Code Agent ───────────────────────────
|
|
571
|
+
await runAgentTask(text, {
|
|
572
|
+
appendMessage, streamMessage, setThinking,
|
|
573
|
+
requestApproval, askUser, setMode, appendCodeStep
|
|
574
|
+
}, sharedState);
|
|
575
|
+
} finally {
|
|
576
|
+
sharedState.isBusy = false;
|
|
577
|
+
}
|
|
426
578
|
},
|
|
427
579
|
|
|
428
|
-
onExit: () =>
|
|
580
|
+
onExit: () => {
|
|
581
|
+
if (ui && typeof ui.unmount === 'function') ui.unmount();
|
|
582
|
+
exitWithGoodbye(0, buildExitSummary(sharedState.stats));
|
|
583
|
+
}
|
|
429
584
|
});
|
|
430
585
|
|
|
431
586
|
// ── Handle initial CLI --image option ───────────────────────────────────
|
|
@@ -434,7 +589,8 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
|
434
589
|
const image = loadImageAsDataUri(options.imagePath);
|
|
435
590
|
const prompt = initialMessage || 'Analyze this image.';
|
|
436
591
|
const { responseText, labels, imageList } = await sendImageMessage({
|
|
437
|
-
images: [image], prompt, appendMessage, streamMessage, setThinking, appendCodeStep
|
|
592
|
+
images: [image], prompt, appendMessage, streamMessage, setThinking, appendCodeStep,
|
|
593
|
+
stats: sharedState.stats
|
|
438
594
|
});
|
|
439
595
|
sharedState.lastResponseText = responseText;
|
|
440
596
|
if (responseText) {
|
|
@@ -476,4 +632,10 @@ async function startInteractiveChat(initialMessage = null, options = {}) {
|
|
|
476
632
|
}
|
|
477
633
|
}
|
|
478
634
|
|
|
479
|
-
module.exports = {
|
|
635
|
+
module.exports = {
|
|
636
|
+
startInteractiveChat,
|
|
637
|
+
_helpers: {
|
|
638
|
+
hasAllImageLabels,
|
|
639
|
+
formatImageDisplayMessage
|
|
640
|
+
}
|
|
641
|
+
};
|
|
@@ -169,8 +169,8 @@ async function handleSlashCommandUI(
|
|
|
169
169
|
try {
|
|
170
170
|
const image = loadImageAsDataUri(imagePath);
|
|
171
171
|
if (helpers.attachImage) {
|
|
172
|
-
helpers.attachImage({ label: image.path, image });
|
|
173
172
|
if (prompt && helpers.setInputText) helpers.setInputText(prompt);
|
|
173
|
+
helpers.attachImage({ label: image.path, image });
|
|
174
174
|
appendMessage('system', 'Attached image. Press Enter to send.');
|
|
175
175
|
} else {
|
|
176
176
|
appendMessage('error', 'Image attachment is not available in this UI.');
|
|
@@ -186,9 +186,9 @@ async function handleSlashCommandUI(
|
|
|
186
186
|
try {
|
|
187
187
|
const image = loadClipboardImageAsDataUri();
|
|
188
188
|
if (helpers.attachImage) {
|
|
189
|
-
helpers.attachImage({ label: image.path, image });
|
|
190
189
|
const prompt = args.join(' ').trim();
|
|
191
190
|
if (prompt && helpers.setInputText) helpers.setInputText(prompt);
|
|
191
|
+
helpers.attachImage({ label: image.path, image });
|
|
192
192
|
appendMessage('system', 'Attached clipboard image. Press Enter to send.');
|
|
193
193
|
} else {
|
|
194
194
|
appendMessage('error', 'Image attachment is not available in this UI.');
|
package/src/CLI/updater.js
CHANGED
|
@@ -112,6 +112,25 @@ function formatUpdateError(error) {
|
|
|
112
112
|
return `Update failed: ${detail || 'Unknown npm error'}`;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
function formatUpdateCheckError(error) {
|
|
116
|
+
const detail = [error.stderr, error.stdout, error.message].filter(Boolean).join('\n').trim();
|
|
117
|
+
if (/E404|404 Not Found|not in this registry/i.test(detail)) {
|
|
118
|
+
return [
|
|
119
|
+
`Update check unavailable: ${pkg.name} is not published on the npm registry.`,
|
|
120
|
+
'This does not mean your local Mint version is outdated.'
|
|
121
|
+
].join('\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (/ENOTFOUND|ETIMEDOUT|ECONNRESET|ECONNREFUSED|network|timeout/i.test(detail)) {
|
|
125
|
+
return [
|
|
126
|
+
'Update check unavailable: npm registry could not be reached.',
|
|
127
|
+
'This does not mean your local Mint version is outdated.'
|
|
128
|
+
].join('\n');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return `Update check unavailable: ${detail || 'Unknown npm error'}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
115
134
|
async function runUpdate(options = {}) {
|
|
116
135
|
const currentVersion = pkg.version;
|
|
117
136
|
let latestVersion = '';
|
|
@@ -123,7 +142,7 @@ async function runUpdate(options = {}) {
|
|
|
123
142
|
status: 'error',
|
|
124
143
|
currentVersion,
|
|
125
144
|
latestVersion,
|
|
126
|
-
message:
|
|
145
|
+
message: formatUpdateCheckError(error)
|
|
127
146
|
};
|
|
128
147
|
}
|
|
129
148
|
|
|
@@ -205,6 +224,7 @@ module.exports = {
|
|
|
205
224
|
_private: {
|
|
206
225
|
parseVersion,
|
|
207
226
|
formatUpdateError,
|
|
227
|
+
formatUpdateCheckError,
|
|
208
228
|
getAutoUpdateIntervalMs
|
|
209
229
|
}
|
|
210
230
|
};
|