@pheem49/mint 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex +0 -0
- package/README.md +174 -126
- package/main.js +21 -1
- package/mint-cli-logic.js +21 -1
- package/mint-cli.js +287 -45
- package/package.json +13 -2
- package/src/AI_Brain/Gemini_API.js +331 -64
- package/src/AI_Brain/agent_orchestrator.js +73 -0
- package/src/AI_Brain/autonomous_brain.js +2 -0
- package/src/AI_Brain/memory_store.js +318 -0
- package/src/AI_Brain/proactive_engine.js +2 -8
- package/src/Automation_Layer/file_operations.js +123 -4
- package/src/Automation_Layer/open_app.js +72 -43
- package/src/Automation_Layer/open_website.js +3 -3
- package/src/CLI/chat_router.js +57 -9
- package/src/CLI/chat_ui.js +117 -11
- package/src/CLI/code_agent.js +249 -36
- package/src/CLI/onboarding.js +53 -6
- package/src/CLI/workspace_manager.js +90 -0
- package/src/Plugins/docker.js +12 -10
- package/src/Plugins/spotify.js +168 -40
- package/src/Plugins/system_monitor.js +72 -0
- package/src/System/config_manager.js +35 -2
- package/src/System/custom_workflows.js +9 -2
- package/src/System/notifications.js +23 -0
- package/src/UI/settings.html +143 -65
- package/src/UI/settings.js +155 -41
- package/tests/agent_orchestrator.test.js +41 -0
- package/tests/chat_router.test.js +42 -0
- package/tests/code_agent.test.js +69 -0
- package/tests/config_manager.test.js +141 -0
- package/tests/docker.test.js +46 -0
- package/tests/file_operations.test.js +57 -0
- package/tests/memory_store.test.js +185 -0
- package/tests/provider_routing.test.js +67 -0
- package/tests/spotify.test.js +201 -0
- package/tests/system_monitor.test.js +37 -0
- package/tests/workspace_manager.test.js +56 -0
package/mint-cli.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
require('dotenv').config({ quiet: true });
|
|
3
|
+
// Suppress experimental SQLite warning
|
|
4
|
+
const originalEmit = process.emit;
|
|
5
|
+
process.emit = function (name, data, ...args) {
|
|
6
|
+
if (name === 'warning' && typeof data === 'object' && data.name === 'ExperimentalWarning' && data.message.includes('SQLite')) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return originalEmit.apply(process, [name, data, ...args]);
|
|
10
|
+
};
|
|
3
11
|
const { Command } = require('commander');
|
|
4
|
-
const { handleChat, resetChat } = require('./src/AI_Brain/Gemini_API');
|
|
12
|
+
const { handleChat, handleGeminiChatStream, resetChat, refreshApiKeyFromConfig, getChatTranscript } = require('./src/AI_Brain/Gemini_API');
|
|
13
|
+
const agentOrchestrator = require('./src/AI_Brain/agent_orchestrator');
|
|
14
|
+
const workspaceManager = require('./src/CLI/workspace_manager');
|
|
15
|
+
const systemMonitor = require('./src/Plugins/system_monitor');
|
|
16
|
+
const { sendNotification } = require('./src/System/notifications');
|
|
5
17
|
const pkg = require('./package.json');
|
|
6
18
|
const { runOnboarding } = require('./src/CLI/onboarding');
|
|
7
19
|
const { startAgent } = require('./src/AI_Brain/headless_agent');
|
|
@@ -128,27 +140,117 @@ program.parse(process.argv);
|
|
|
128
140
|
* The Interactive Chat Loop — Gemini-style TUI
|
|
129
141
|
*/
|
|
130
142
|
async function startInteractiveChat(initialMessage = null) {
|
|
131
|
-
|
|
143
|
+
let lastResponseText = "";
|
|
144
|
+
const { screen, appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode } = createChatUI({
|
|
132
145
|
onSubmit: async (text) => {
|
|
133
146
|
if (text.startsWith('/')) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
if (text.startsWith('/agent')) {
|
|
148
|
+
const args = text.split(' ');
|
|
149
|
+
if (args[1] === 'list') {
|
|
150
|
+
appendMessage('system', `Available Agents: ${agentOrchestrator.listAgents().join(', ')}`);
|
|
151
|
+
} else if (args[1]) {
|
|
152
|
+
const success = agentOrchestrator.setAgent(args[1]);
|
|
153
|
+
if (success) {
|
|
154
|
+
const agent = agentOrchestrator.getCurrentAgent();
|
|
155
|
+
appendMessage('system', `Switched to Agent: ${agent.icon} ${agent.name}`);
|
|
156
|
+
updateStatusModel(agent.name); // Pass name to status bar
|
|
157
|
+
resetChat(); // Reset to apply new system prompt
|
|
158
|
+
} else {
|
|
159
|
+
appendMessage('error', `Agent "${args[1]}" not found. Try /agent list`);
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
const agent = agentOrchestrator.getCurrentAgent();
|
|
163
|
+
appendMessage('system', `Current Agent: ${agent.icon} ${agent.name}\nUsage: /agent <type> or /agent list`);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (text.startsWith('/stats')) {
|
|
169
|
+
appendMessage('system', '📊 Fetching system statistics...');
|
|
170
|
+
const stats = await systemMonitor.execute('stats');
|
|
171
|
+
appendMessage('system', stats);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (text.startsWith('/workspace')) {
|
|
176
|
+
const args = text.split(' ');
|
|
177
|
+
const subCmd = args[1];
|
|
178
|
+
|
|
179
|
+
if (subCmd === 'add') {
|
|
180
|
+
const name = args[2];
|
|
181
|
+
const wsPath = args[3] || '.';
|
|
182
|
+
const instructions = args.slice(4).join(' ');
|
|
183
|
+
if (!name) {
|
|
184
|
+
appendMessage('error', 'Usage: /workspace add <name> [path] [instructions]');
|
|
185
|
+
} else {
|
|
186
|
+
workspaceManager.addWorkspace(name, wsPath, instructions);
|
|
187
|
+
appendMessage('system', `Workspace "${name}" registered at ${path.resolve(wsPath)}`);
|
|
188
|
+
resetChat();
|
|
189
|
+
}
|
|
190
|
+
} else if (subCmd === 'list') {
|
|
191
|
+
const all = workspaceManager.listWorkspaces();
|
|
192
|
+
let listMsg = "Registered Workspaces:\n";
|
|
193
|
+
for (const n in all) listMsg += `- ${n}: ${all[n].path}\n`;
|
|
194
|
+
appendMessage('system', Object.keys(all).length ? listMsg : "No workspaces registered.");
|
|
195
|
+
} else if (subCmd === 'remove') {
|
|
196
|
+
const name = args[2];
|
|
197
|
+
if (workspaceManager.removeWorkspace(name)) {
|
|
198
|
+
appendMessage('system', `Removed workspace "${name}"`);
|
|
199
|
+
resetChat();
|
|
200
|
+
} else {
|
|
201
|
+
appendMessage('error', `Workspace "${name}" not found.`);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
const ws = workspaceManager.getWorkspaceByPath(process.cwd());
|
|
205
|
+
appendMessage('system', ws ? `Current Workspace: ${ws.name}\nPath: ${ws.path}` : "Not currently in a registered workspace.\nUsage: /workspace <add|list|remove>");
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (text.startsWith('/review')) {
|
|
211
|
+
if (!lastResponseText) {
|
|
212
|
+
appendMessage('error', 'Nothing to review yet. Get a response first.');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
agentOrchestrator.setAgent('reviewer');
|
|
216
|
+
appendMessage('system', '⚖️ Requesting second-pass review from Mint Reviewer...');
|
|
217
|
+
text = `Please review this previous response and provide a critique:\n\n${lastResponseText}`;
|
|
218
|
+
} else {
|
|
219
|
+
// Other slash commands
|
|
220
|
+
const fakeRl = { close: () => { } };
|
|
221
|
+
appendMessage('user', text);
|
|
222
|
+
await handleSlashCommandUI(text, appendMessage, updateStatusModel, copyLastResponse, setThinking, requestApproval, setMode);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
139
225
|
}
|
|
140
226
|
appendMessage('user', text);
|
|
141
227
|
|
|
142
|
-
const
|
|
228
|
+
const transcript = await getChatTranscript();
|
|
229
|
+
const routeDecision = await detectCodeIntent(text, process.cwd(), transcript);
|
|
143
230
|
if (routeDecision.route === 'code') {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
231
|
+
const approved = await requestApproval({
|
|
232
|
+
type: 'code_mode',
|
|
233
|
+
label: 'Mint wants to switch this request into Code Mode.',
|
|
234
|
+
preview: [
|
|
235
|
+
`Request: ${text}`,
|
|
236
|
+
`Reason: ${routeDecision.reason}`,
|
|
237
|
+
'',
|
|
238
|
+
'Code Mode is better for larger coding tasks that may inspect the workspace, run checks, or edit files.'
|
|
239
|
+
].join('\n')
|
|
150
240
|
});
|
|
151
|
-
|
|
241
|
+
if (!approved) {
|
|
242
|
+
appendMessage('system', `Router stayed in Chat Mode. ${routeDecision.reason}`);
|
|
243
|
+
} else {
|
|
244
|
+
appendMessage('system', `Router: entering Code Mode. ${routeDecision.reason}`);
|
|
245
|
+
await runChatRoutedTask(text, {
|
|
246
|
+
appendMessage,
|
|
247
|
+
setThinking,
|
|
248
|
+
requestApproval,
|
|
249
|
+
setMode,
|
|
250
|
+
history: transcript
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
152
254
|
}
|
|
153
255
|
|
|
154
256
|
setMode('Chat');
|
|
@@ -162,16 +264,92 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
162
264
|
}, 1000);
|
|
163
265
|
|
|
164
266
|
try {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
267
|
+
const config = require('./src/System/config_manager').readConfig();
|
|
268
|
+
const provider = config.aiProvider || 'gemini';
|
|
269
|
+
const currentAgent = agentOrchestrator.getCurrentAgent();
|
|
270
|
+
updateStatusModel(currentAgent.name);
|
|
271
|
+
if (provider === 'gemini') {
|
|
272
|
+
// ── Streaming path (Gemini only) ──────────────────────────────────
|
|
273
|
+
// Gemini returns JSON so we buffer all chunks and progressively
|
|
274
|
+
// extract the "response" field as more of the JSON arrives.
|
|
275
|
+
clearInterval(timer);
|
|
276
|
+
|
|
277
|
+
let jsonBuffer = '';
|
|
278
|
+
let finalParsed = null;
|
|
279
|
+
let streamer = null;
|
|
280
|
+
let displayedChars = 0; // chars of response text already sent to TUI
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
for await (const event of handleGeminiChatStream(text)) {
|
|
284
|
+
if (event.chunk) {
|
|
285
|
+
jsonBuffer += event.chunk;
|
|
286
|
+
|
|
287
|
+
// Progressively extract readable text from the growing JSON buffer
|
|
288
|
+
const match = jsonBuffer.match(/"response"\s*:\s*"((?:[^"\\]|\\.)*)"/s);
|
|
289
|
+
if (match) {
|
|
290
|
+
const fullText = match[1]
|
|
291
|
+
.replace(/\\n/g, '\n')
|
|
292
|
+
.replace(/\\"/g, '"')
|
|
293
|
+
.replace(/\\\\/g, '\\');
|
|
294
|
+
const newChars = fullText.slice(displayedChars);
|
|
295
|
+
if (newChars.length > 0) {
|
|
296
|
+
if (!streamer) {
|
|
297
|
+
setThinking(false);
|
|
298
|
+
streamer = streamMessage('assistant');
|
|
299
|
+
}
|
|
300
|
+
streamer.appendChunk(newChars);
|
|
301
|
+
displayedChars = fullText.length;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} else if (event.done) {
|
|
305
|
+
finalParsed = event.parsed;
|
|
306
|
+
// Flush any remaining response text not yet displayed
|
|
307
|
+
if (finalParsed && finalParsed.response) {
|
|
308
|
+
const remaining = finalParsed.response.slice(displayedChars);
|
|
309
|
+
if (!streamer) {
|
|
310
|
+
setThinking(false);
|
|
311
|
+
streamer = streamMessage('assistant');
|
|
312
|
+
}
|
|
313
|
+
if (remaining) streamer.appendChunk(remaining);
|
|
314
|
+
}
|
|
315
|
+
if (streamer) {
|
|
316
|
+
streamer.finalize(event.timestamp);
|
|
317
|
+
} else {
|
|
318
|
+
setThinking(false);
|
|
319
|
+
appendMessage('assistant',
|
|
320
|
+
finalParsed ? finalParsed.response : '',
|
|
321
|
+
event.timestamp);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
} catch (streamErr) {
|
|
326
|
+
setThinking(false);
|
|
327
|
+
appendMessage('error', streamErr.message);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Execute Actions from the final parsed response
|
|
332
|
+
if (finalParsed) {
|
|
333
|
+
const { executeAction } = require('./mint-cli-logic');
|
|
334
|
+
if (finalParsed.action && finalParsed.action.type !== 'none') {
|
|
335
|
+
const result = await executeAction(finalParsed.action);
|
|
336
|
+
if (result) appendMessage('system', `Action: ${result}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
169
339
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
340
|
+
} else {
|
|
341
|
+
// ── Non-streaming fallback (Ollama, Anthropic, OpenAI, etc.) ──
|
|
342
|
+
const response = await handleChat(text);
|
|
343
|
+
clearInterval(timer);
|
|
344
|
+
setThinking(false);
|
|
345
|
+
lastResponseText = response.response;
|
|
346
|
+
appendMessage('assistant', response.response, response.timestamp);
|
|
347
|
+
|
|
348
|
+
const { executeAction } = require('./mint-cli-logic');
|
|
349
|
+
if (response.action && response.action.type !== 'none') {
|
|
350
|
+
const result = await executeAction(response.action);
|
|
351
|
+
if (result) appendMessage('system', `Action: ${result}`);
|
|
352
|
+
}
|
|
175
353
|
}
|
|
176
354
|
} catch (err) {
|
|
177
355
|
clearInterval(timer);
|
|
@@ -192,16 +370,52 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
192
370
|
// Handle initial message if passed via CLI arg
|
|
193
371
|
if (initialMessage) {
|
|
194
372
|
appendMessage('user', initialMessage);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
373
|
+
const transcript = await getChatTranscript();
|
|
374
|
+
const routeDecision = await detectCodeIntent(initialMessage, process.cwd(), transcript);
|
|
375
|
+
if (routeDecision.route === 'code') {
|
|
376
|
+
const approved = await requestApproval({
|
|
377
|
+
type: 'code_mode',
|
|
378
|
+
label: 'Mint wants to switch this request into Code Mode.',
|
|
379
|
+
preview: [
|
|
380
|
+
`Request: ${initialMessage}`,
|
|
381
|
+
`Reason: ${routeDecision.reason}`,
|
|
382
|
+
'',
|
|
383
|
+
'Code Mode is better for larger coding tasks that may inspect the workspace, run checks, or edit files.'
|
|
384
|
+
].join('\n')
|
|
385
|
+
});
|
|
386
|
+
if (approved) {
|
|
387
|
+
appendMessage('system', `Router: entering Code Mode. ${routeDecision.reason}`);
|
|
388
|
+
await runChatRoutedTask(initialMessage, {
|
|
389
|
+
appendMessage,
|
|
390
|
+
setThinking,
|
|
391
|
+
requestApproval,
|
|
392
|
+
setMode,
|
|
393
|
+
history: transcript
|
|
394
|
+
});
|
|
395
|
+
} else {
|
|
396
|
+
appendMessage('system', `Router stayed in Chat Mode. ${routeDecision.reason}`);
|
|
397
|
+
setMode('Chat');
|
|
398
|
+
let seconds = 0;
|
|
399
|
+
setThinking(true, seconds);
|
|
400
|
+
const timer = setInterval(() => { seconds++; setThinking(true, seconds); }, 1000);
|
|
401
|
+
try {
|
|
402
|
+
const response = await handleChat(initialMessage);
|
|
403
|
+
clearInterval(timer);
|
|
404
|
+
setThinking(false);
|
|
405
|
+
appendMessage('assistant', response.response, response.timestamp);
|
|
406
|
+
lastResponseText = response.response;
|
|
407
|
+
const { executeAction } = require('./mint-cli-logic');
|
|
408
|
+
if (response.action && response.action.type !== 'none') {
|
|
409
|
+
const result = await executeAction(response.action);
|
|
410
|
+
if (result) appendMessage('system', `Action: ${result}`);
|
|
411
|
+
}
|
|
412
|
+
} catch (err) {
|
|
413
|
+
clearInterval(timer);
|
|
414
|
+
setThinking(false);
|
|
415
|
+
appendMessage('error', err.message);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
205
419
|
setMode('Chat');
|
|
206
420
|
let seconds = 0;
|
|
207
421
|
setThinking(true, seconds);
|
|
@@ -211,6 +425,12 @@ async function startInteractiveChat(initialMessage = null) {
|
|
|
211
425
|
clearInterval(timer);
|
|
212
426
|
setThinking(false);
|
|
213
427
|
appendMessage('assistant', response.response, response.timestamp);
|
|
428
|
+
lastResponseText = response.response;
|
|
429
|
+
const { executeAction } = require('./mint-cli-logic');
|
|
430
|
+
if (response.action && response.action.type !== 'none') {
|
|
431
|
+
const result = await executeAction(response.action);
|
|
432
|
+
if (result) appendMessage('system', `Action: ${result}`);
|
|
433
|
+
}
|
|
214
434
|
} catch (err) {
|
|
215
435
|
clearInterval(timer);
|
|
216
436
|
setThinking(false);
|
|
@@ -248,26 +468,47 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
248
468
|
const config = readConfig();
|
|
249
469
|
if (args.length === 0) {
|
|
250
470
|
appendMessage('system', [
|
|
251
|
-
`Current
|
|
252
|
-
|
|
253
|
-
'
|
|
254
|
-
' - gemini-
|
|
255
|
-
' -
|
|
256
|
-
' -
|
|
471
|
+
`Current Provider: ${config.aiProvider}`,
|
|
472
|
+
`Current Gemini Model: ${config.geminiModel}`,
|
|
473
|
+
'Available Providers/Presets:',
|
|
474
|
+
' - gemini-2.5-flash (Default Gemini)',
|
|
475
|
+
' - ollama (Local provider)',
|
|
476
|
+
' - anthropic (Claude)',
|
|
477
|
+
' - openai (GPT)',
|
|
478
|
+
' - huggingface (Inference API)',
|
|
479
|
+
' - local (LM Studio / OpenAI Compatible)',
|
|
257
480
|
'Usage: /models <name> to switch'
|
|
258
481
|
].join('\n'));
|
|
259
482
|
} else {
|
|
260
483
|
const { writeConfig } = require('./src/System/config_manager');
|
|
261
484
|
const newModel = args[0];
|
|
485
|
+
let newProvider = 'gemini';
|
|
486
|
+
|
|
262
487
|
if (newModel === 'ollama') {
|
|
263
|
-
|
|
488
|
+
newProvider = 'ollama';
|
|
489
|
+
} else if (newModel === 'anthropic') {
|
|
490
|
+
newProvider = 'anthropic';
|
|
491
|
+
} else if (newModel === 'openai') {
|
|
492
|
+
newProvider = 'openai';
|
|
493
|
+
} else if (newModel === 'huggingface') {
|
|
494
|
+
newProvider = 'huggingface';
|
|
495
|
+
} else if (newModel === 'local' || newModel === 'local_openai') {
|
|
496
|
+
newProvider = 'local_openai';
|
|
497
|
+
} else if (newModel.startsWith('gpt-')) {
|
|
498
|
+
newProvider = 'openai';
|
|
499
|
+
config.openaiModel = newModel;
|
|
500
|
+
} else if (newModel.startsWith('claude-')) {
|
|
501
|
+
newProvider = 'anthropic';
|
|
502
|
+
config.anthropicModel = newModel;
|
|
264
503
|
} else {
|
|
265
|
-
|
|
504
|
+
newProvider = 'gemini';
|
|
266
505
|
config.geminiModel = newModel;
|
|
267
506
|
}
|
|
507
|
+
|
|
508
|
+
config.aiProvider = newProvider;
|
|
268
509
|
writeConfig(config);
|
|
269
|
-
appendMessage('system', `✅ Switched to: ${newModel}`);
|
|
270
|
-
if (updateStatusModel) updateStatusModel(newModel);
|
|
510
|
+
appendMessage('system', `✅ Switched to: ${newProvider} ${newProvider === 'gemini' ? `(${newModel})` : ''}`);
|
|
511
|
+
if (updateStatusModel) updateStatusModel(newProvider === 'gemini' ? newModel : newProvider);
|
|
271
512
|
}
|
|
272
513
|
break;
|
|
273
514
|
|
|
@@ -280,7 +521,8 @@ async function handleSlashCommandUI(input, appendMessage, updateStatusModel, cop
|
|
|
280
521
|
appendMessage,
|
|
281
522
|
setThinking,
|
|
282
523
|
requestApproval,
|
|
283
|
-
setMode
|
|
524
|
+
setMode,
|
|
525
|
+
history: await getChatTranscript()
|
|
284
526
|
});
|
|
285
527
|
break;
|
|
286
528
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pheem49/mint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
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": {
|
|
7
7
|
"start": "electron .",
|
|
8
|
-
"test": "
|
|
8
|
+
"test": "jest --testPathPatterns=tests/",
|
|
9
|
+
"test:watch": "jest --testPathPatterns=tests/ --watch",
|
|
9
10
|
"build:linux": "electron-builder --linux",
|
|
10
11
|
"cli": "node mint-cli.js"
|
|
11
12
|
},
|
|
13
|
+
"jest": {
|
|
14
|
+
"testEnvironment": "node",
|
|
15
|
+
"testMatch": ["**/tests/**/*.test.js"],
|
|
16
|
+
"collectCoverageFrom": [
|
|
17
|
+
"src/AI_Brain/memory_store.js",
|
|
18
|
+
"src/AI_Brain/knowledge_base.js",
|
|
19
|
+
"src/System/config_manager.js"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
12
22
|
"bin": {
|
|
13
23
|
"mint": "mint-cli.js"
|
|
14
24
|
},
|
|
@@ -45,6 +55,7 @@
|
|
|
45
55
|
"@vitejs/plugin-react": "^6.0.1",
|
|
46
56
|
"electron": "^40.7.0",
|
|
47
57
|
"electron-builder": "^26.8.1",
|
|
58
|
+
"jest": "^30.4.0",
|
|
48
59
|
"vite": "^8.0.10"
|
|
49
60
|
},
|
|
50
61
|
"build": {
|