@pheem49/mint 1.4.1 → 1.4.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 +82 -173
- package/mint-cli-logic.js +2 -7
- package/mint-cli.js +210 -191
- package/package.json +1 -1
- package/src/AI_Brain/agent_orchestrator.js +6 -6
- package/src/Automation_Layer/file_operations.js +14 -3
- package/src/CLI/chat_router.js +18 -5
- package/src/CLI/chat_ui.js +163 -34
- package/src/CLI/code_agent.js +230 -86
- package/src/CLI/list_features.js +3 -1
- package/src/System/config_manager.js +1 -1
package/src/CLI/chat_ui.js
CHANGED
|
@@ -32,7 +32,7 @@ const SLASH_COMMANDS = [
|
|
|
32
32
|
function createChatUI({ onSubmit, onExit }) {
|
|
33
33
|
const config = readConfig();
|
|
34
34
|
const modelName = config.geminiModel || 'gemini';
|
|
35
|
-
const
|
|
35
|
+
const workspacePath = process.cwd();
|
|
36
36
|
const HINT_DEFAULT = `{gray-fg} Enter send · Ctrl+Y copy · /help commands{/}`;
|
|
37
37
|
const INPUT_FG = '#f8fafc';
|
|
38
38
|
const INPUT_BG = '#10141c';
|
|
@@ -178,7 +178,7 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
178
178
|
} catch (_) {}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
// ─── Status bar (
|
|
181
|
+
// ─── Status bar (2 lines as per screenshot) ──────────────────────
|
|
182
182
|
const statusBar = blessed.box({
|
|
183
183
|
bottom: 0, left: 1, width: '100%-2', height: 3,
|
|
184
184
|
tags: true,
|
|
@@ -186,54 +186,62 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
186
186
|
border: { type: 'line', fg: '#222c38' }
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
-
//
|
|
190
|
-
const
|
|
189
|
+
// Line 1: Thinking / Status (Left) and Shortcut (Right)
|
|
190
|
+
const statusLine1 = blessed.text({
|
|
191
191
|
parent: statusBar,
|
|
192
|
-
top: 0, left: 1,
|
|
193
|
-
width: '33%',
|
|
192
|
+
top: 0, left: 1, right: 1,
|
|
194
193
|
height: 1,
|
|
195
194
|
tags: true,
|
|
196
|
-
content: `
|
|
197
|
-
style: { bg: '#10141c'
|
|
195
|
+
content: `{#88aaff-fg}[Chat]{/} {#cc4444-fg}no sandbox{/}`,
|
|
196
|
+
style: { bg: '#10141c' }
|
|
198
197
|
});
|
|
199
198
|
|
|
200
|
-
|
|
201
|
-
const statusCenter = blessed.text({
|
|
199
|
+
const shortcutHint = blessed.text({
|
|
202
200
|
parent: statusBar,
|
|
203
|
-
top: 0,
|
|
204
|
-
left: 'center',
|
|
205
|
-
width: '44%',
|
|
201
|
+
top: 0, right: 1,
|
|
206
202
|
height: 1,
|
|
207
|
-
align: 'center',
|
|
208
203
|
tags: true,
|
|
209
|
-
content: `{
|
|
210
|
-
style: { bg: '#10141c'
|
|
204
|
+
content: `{gray-fg}? for shortcuts{/}`,
|
|
205
|
+
style: { bg: '#10141c' }
|
|
211
206
|
});
|
|
212
207
|
|
|
213
|
-
//
|
|
214
|
-
const
|
|
208
|
+
// Line 2: Action Hint (Left) and File Info (Right)
|
|
209
|
+
const statusLine2 = blessed.text({
|
|
215
210
|
parent: statusBar,
|
|
216
|
-
top:
|
|
217
|
-
width: '33%',
|
|
211
|
+
top: 1, left: 1, right: 1,
|
|
218
212
|
height: 1,
|
|
219
|
-
align: 'right',
|
|
220
213
|
tags: true,
|
|
221
|
-
content: `{
|
|
222
|
-
style: { bg: '#10141c'
|
|
214
|
+
content: `{gray-fg}Shift+Tab to accept edits{/}`,
|
|
215
|
+
style: { bg: '#10141c' }
|
|
223
216
|
});
|
|
224
217
|
|
|
225
|
-
|
|
218
|
+
const fileInfo = blessed.text({
|
|
219
|
+
parent: statusBar,
|
|
220
|
+
top: 1, right: 1,
|
|
221
|
+
height: 1,
|
|
222
|
+
tags: true,
|
|
223
|
+
content: `{gray-fg}path: ${workspacePath}{/}`,
|
|
224
|
+
style: { bg: '#10141c' }
|
|
225
|
+
});
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
let activeMode = 'Chat';
|
|
228
|
+
let spinnerIdx = 0;
|
|
229
|
+
const spinnerChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
230
|
+
|
|
231
|
+
function formatTime(seconds) {
|
|
232
|
+
if (seconds < 60) return `${seconds}s`;
|
|
233
|
+
const mins = Math.floor(seconds / 60);
|
|
234
|
+
const secs = seconds % 60;
|
|
235
|
+
return `${mins}m ${secs}s`;
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
function updateStatusBar(thinkingText = null) {
|
|
238
|
+
function updateStatusBar(thinkingText = null, secondsElapsed = 0) {
|
|
233
239
|
if (thinkingText) {
|
|
234
|
-
|
|
240
|
+
const char = spinnerChars[spinnerIdx % spinnerChars.length];
|
|
241
|
+
spinnerIdx++;
|
|
242
|
+
statusLine1.setContent(`{#88e0b0-fg}${char}{/} Thinking... {gray-fg}(esc to cancel, ${formatTime(secondsElapsed)}){/}`);
|
|
235
243
|
} else {
|
|
236
|
-
|
|
244
|
+
statusLine1.setContent(`${activeMode === 'Code' ? '{#ffd166-fg}[Code]{/}' : '{#88aaff-fg}[Chat]{/}'} {#cc4444-fg}no sandbox{/}`);
|
|
237
245
|
}
|
|
238
246
|
screen.render();
|
|
239
247
|
}
|
|
@@ -243,10 +251,16 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
243
251
|
updateStatusBar(null);
|
|
244
252
|
}
|
|
245
253
|
|
|
246
|
-
/** Update model name in status bar (called after /models switch) */
|
|
247
254
|
function updateStatusModel(newModel) {
|
|
248
255
|
if (!newModel) return;
|
|
249
|
-
|
|
256
|
+
shortcutHint.setContent(`{gray-fg}${newModel} · ? for shortcuts{/}`);
|
|
257
|
+
screen.render();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Update workspace name in status bar */
|
|
261
|
+
function updateWorkspace(newPath) {
|
|
262
|
+
if (!newPath) return;
|
|
263
|
+
fileInfo.setContent(`{gray-fg}path: ${newPath}{/}`);
|
|
250
264
|
screen.render();
|
|
251
265
|
}
|
|
252
266
|
updateStatusBar();
|
|
@@ -260,6 +274,8 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
260
274
|
screen.append(statusBar);
|
|
261
275
|
screen.append(placeholderWidget); // sibling on top of inputBox
|
|
262
276
|
|
|
277
|
+
// Suggestion List and Approval Dialog remain same ...
|
|
278
|
+
|
|
263
279
|
// ─── Suggestion List ──────────────────────────────────────────────────────
|
|
264
280
|
const commandList = blessed.list({
|
|
265
281
|
parent: screen,
|
|
@@ -389,6 +405,19 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
389
405
|
applyTerminalInputAttrs();
|
|
390
406
|
});
|
|
391
407
|
|
|
408
|
+
// Restore focus to inputBox when clicked or when screen is clicked
|
|
409
|
+
screen.on('click', () => {
|
|
410
|
+
if (!approvalDialog.visible) {
|
|
411
|
+
inputBox.focus();
|
|
412
|
+
screen.render();
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
inputBox.on('click', () => {
|
|
417
|
+
inputBox.focus();
|
|
418
|
+
screen.render();
|
|
419
|
+
});
|
|
420
|
+
|
|
392
421
|
|
|
393
422
|
// Submit or Select Suggestion on Enter
|
|
394
423
|
inputBox.on('submit', (value) => {
|
|
@@ -684,10 +713,72 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
684
713
|
return { appendChunk, finalize };
|
|
685
714
|
}
|
|
686
715
|
|
|
716
|
+
function appendCodeStep(info) {
|
|
717
|
+
if (typeof info === 'string') {
|
|
718
|
+
appendMessage('system', `[Code] ${info}`);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const { step, phase, action, target, message, thought } = info;
|
|
723
|
+
const maxLineWidth = Math.max(screen.width - 20, 36);
|
|
724
|
+
|
|
725
|
+
// Special handling for ask_user which needs a box style
|
|
726
|
+
if (action === 'ask_user') {
|
|
727
|
+
chatBox.log('');
|
|
728
|
+
chatBox.log(` {#88e0b0-fg}✓{/} {bold}Ask User{/}`);
|
|
729
|
+
const questionLines = wrapText(target || message || '', maxLineWidth - 6);
|
|
730
|
+
questionLines.forEach(l => chatBox.log(` {#95a2b8-fg}${l}{/}`));
|
|
731
|
+
screen.render();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
let icon = '{#88e0b0-fg}✓{/}';
|
|
736
|
+
let label = action || phase;
|
|
737
|
+
let color = '{#ffffff-fg}';
|
|
738
|
+
|
|
739
|
+
// Map internal action names to display names seen in the screenshot
|
|
740
|
+
switch (action) {
|
|
741
|
+
case 'thinking':
|
|
742
|
+
if (phase === 'thinking' && !thought) {
|
|
743
|
+
// Initial "Thinking..." without a bubble
|
|
744
|
+
chatBox.log('');
|
|
745
|
+
chatBox.log(` {#ffd166-fg}* {bold}Thinking{/}`);
|
|
746
|
+
} else if (thought) {
|
|
747
|
+
// Show reasoning bubble
|
|
748
|
+
const thoughtLines = wrapText(thought, maxLineWidth - 6);
|
|
749
|
+
thoughtLines.forEach(l => chatBox.log(` {gray-fg}> ${l}{/}`));
|
|
750
|
+
}
|
|
751
|
+
screen.render();
|
|
752
|
+
return;
|
|
753
|
+
case 'ask_user': label = 'AskUser'; break;
|
|
754
|
+
case 'open_url': label = 'OpenURL'; break;
|
|
755
|
+
case 'open_app': label = 'OpenApp'; break;
|
|
756
|
+
case 'open_file': label = 'OpenFile'; break;
|
|
757
|
+
case 'open_folder': label = 'OpenFolder'; break;
|
|
758
|
+
case 'create_folder': label = 'CreateFolder'; break;
|
|
759
|
+
case 'system_info': label = 'SystemInfo'; break;
|
|
760
|
+
case 'system_automation': label = 'SystemAction'; break;
|
|
761
|
+
case 'web_search': label = 'WebSearch'; break;
|
|
762
|
+
case 'list_files':
|
|
763
|
+
case 'find_path': label = 'Explored'; break;
|
|
764
|
+
case 'read_file': label = 'ReadFile'; break;
|
|
765
|
+
case 'search_code': label = 'SearchText'; break;
|
|
766
|
+
case 'apply_patch':
|
|
767
|
+
case 'write_file': label = 'Edited'; break;
|
|
768
|
+
case 'run_shell': label = 'Ran command'; break;
|
|
769
|
+
case 'json_repair': icon = '*'; label = 'Repairing JSON'; color = '{#ffd166-fg}'; break;
|
|
770
|
+
case 'reviewer_start': label = 'Reviewing'; break;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const content = target || message || '';
|
|
774
|
+
chatBox.log(` ${icon} {bold}${label}{/} ${color}${content}{/}`);
|
|
775
|
+
screen.render();
|
|
776
|
+
}
|
|
777
|
+
|
|
687
778
|
/** Show/hide thinking indicator in status bar */
|
|
688
779
|
function setThinking(active, secondsElapsed = 0) {
|
|
689
780
|
if (active) {
|
|
690
|
-
updateStatusBar(
|
|
781
|
+
updateStatusBar('Thinking...', secondsElapsed);
|
|
691
782
|
} else {
|
|
692
783
|
updateStatusBar(null);
|
|
693
784
|
}
|
|
@@ -734,7 +825,45 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
734
825
|
});
|
|
735
826
|
}
|
|
736
827
|
|
|
737
|
-
|
|
828
|
+
function askUser(question) {
|
|
829
|
+
return new Promise((resolve) => {
|
|
830
|
+
// Temporarily stop reading input so we can capture the answer
|
|
831
|
+
if (inputBox._reading) {
|
|
832
|
+
inputBox.cancel();
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// We use a simple textbox floating over the chat or reuse the main input?
|
|
836
|
+
// Reusing the main input is cleaner for CLI.
|
|
837
|
+
// But we need to change the label to ' Answer '
|
|
838
|
+
const oldLabel = inputBox._label.content;
|
|
839
|
+
inputBox._label.setContent(' Answer ');
|
|
840
|
+
|
|
841
|
+
// Clear input for the answer
|
|
842
|
+
inputBox.clearValue();
|
|
843
|
+
hidePlaceholder();
|
|
844
|
+
inputBox.focus();
|
|
845
|
+
inputBox.readInput();
|
|
846
|
+
screen.render();
|
|
847
|
+
|
|
848
|
+
const submitHandler = (value) => {
|
|
849
|
+
inputBox.removeListener('submit', submitHandler);
|
|
850
|
+
inputBox._label.setContent(oldLabel);
|
|
851
|
+
|
|
852
|
+
const answer = value || '';
|
|
853
|
+
chatBox.log('');
|
|
854
|
+
chatBox.log(` {bold}User answered:{/}`);
|
|
855
|
+
const lines = wrapText(answer, screen.width - 20);
|
|
856
|
+
lines.forEach(l => chatBox.log(` {#95a2b8-fg}${l}{/}`));
|
|
857
|
+
screen.render();
|
|
858
|
+
|
|
859
|
+
resolve(answer);
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
inputBox.on('submit', submitHandler);
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return { screen, appendMessage, streamMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode, appendCodeStep, updateWorkspace, askUser };
|
|
738
867
|
}
|
|
739
868
|
|
|
740
869
|
module.exports = { createChatUI };
|