@indiccoder/mentis-cli 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/repl/ReplManager.js +19 -31
- package/dist/ui/InputBox.js +127 -0
- package/dist/ui/UIManager.js +2 -2
- package/package.json +3 -1
- package/src/repl/ReplManager.ts +19 -33
- package/src/ui/InputBox.ts +145 -0
- package/src/ui/UIManager.ts +2 -2
package/dist/repl/ReplManager.js
CHANGED
|
@@ -44,6 +44,7 @@ const ConfigManager_1 = require("../config/ConfigManager");
|
|
|
44
44
|
const OpenAIClient_1 = require("../llm/OpenAIClient");
|
|
45
45
|
const ContextManager_1 = require("../context/ContextManager");
|
|
46
46
|
const UIManager_1 = require("../ui/UIManager");
|
|
47
|
+
const InputBox_1 = require("../ui/InputBox");
|
|
47
48
|
const FileTools_1 = require("../tools/FileTools");
|
|
48
49
|
const SearchTools_1 = require("../tools/SearchTools");
|
|
49
50
|
const PersistentShellTool_1 = require("../tools/PersistentShellTool");
|
|
@@ -224,46 +225,33 @@ class ReplManager {
|
|
|
224
225
|
}
|
|
225
226
|
catch (e) { }
|
|
226
227
|
}
|
|
228
|
+
// Initialize InputBox with history
|
|
229
|
+
const inputBox = new InputBox_1.InputBox(commandHistory);
|
|
227
230
|
while (true) {
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
historySize: 1000,
|
|
240
|
-
prompt: promptText
|
|
241
|
-
});
|
|
242
|
-
rl.prompt();
|
|
243
|
-
rl.on('line', (line) => {
|
|
244
|
-
rl.close();
|
|
245
|
-
resolve(line);
|
|
246
|
-
});
|
|
231
|
+
// Calculate context usage for display
|
|
232
|
+
const usage = this.contextVisualizer.calculateUsage(this.history);
|
|
233
|
+
// Display enhanced input frame
|
|
234
|
+
inputBox.displayFrame({
|
|
235
|
+
messageCount: this.history.length,
|
|
236
|
+
contextPercent: usage.percentage
|
|
237
|
+
});
|
|
238
|
+
// Get styled input
|
|
239
|
+
const answer = await inputBox.prompt({
|
|
240
|
+
showHint: this.history.length === 0,
|
|
241
|
+
hint: 'Type your message or /help for commands'
|
|
247
242
|
});
|
|
248
|
-
// Update history manually or grab from rl?
|
|
249
|
-
// rl.history gets updated when user hits enter.
|
|
250
|
-
// But we closed rl. We should manually save the input to our tracking array and file.
|
|
251
243
|
const input = answer.trim();
|
|
252
244
|
if (input) {
|
|
253
|
-
// Update
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
if (commandHistory[0] !== input) {
|
|
257
|
-
commandHistory.unshift(input);
|
|
258
|
-
}
|
|
259
|
-
// Append to file (as standard log, so append at end)
|
|
245
|
+
// Update history via InputBox
|
|
246
|
+
inputBox.addToHistory(input);
|
|
247
|
+
// Append to file
|
|
260
248
|
try {
|
|
261
249
|
fs.appendFileSync(HISTORY_FILE, input + '\n');
|
|
262
250
|
}
|
|
263
251
|
catch (e) { }
|
|
264
252
|
}
|
|
265
|
-
if (!
|
|
266
|
-
continue;
|
|
253
|
+
if (!input)
|
|
254
|
+
continue;
|
|
267
255
|
if (input.startsWith('/')) {
|
|
268
256
|
await this.handleCommand(input);
|
|
269
257
|
continue;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* InputBox - Simple clean input with top line only
|
|
4
|
+
* Bottom line appears after submission (cross-platform compatible)
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.InputBox = void 0;
|
|
11
|
+
const readline_1 = __importDefault(require("readline"));
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
class InputBox {
|
|
14
|
+
constructor(history = []) {
|
|
15
|
+
this.history = [];
|
|
16
|
+
this.historySize = 1000;
|
|
17
|
+
this.history = history;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get terminal width
|
|
21
|
+
*/
|
|
22
|
+
getTerminalWidth() {
|
|
23
|
+
return process.stdout.columns || 80;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create horizontal line
|
|
27
|
+
*/
|
|
28
|
+
createLine() {
|
|
29
|
+
const width = this.getTerminalWidth();
|
|
30
|
+
return chalk_1.default.gray('─'.repeat(width));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get user input with horizontal lines around it
|
|
34
|
+
*/
|
|
35
|
+
async prompt(options = {}) {
|
|
36
|
+
const { showHint = false, hint } = options;
|
|
37
|
+
// Display top horizontal line
|
|
38
|
+
console.log(this.createLine());
|
|
39
|
+
// Display hint if provided
|
|
40
|
+
if (showHint && hint) {
|
|
41
|
+
console.log(chalk_1.default.dim(` ${hint}`));
|
|
42
|
+
}
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
// Create readline with simple prompt
|
|
45
|
+
const rl = readline_1.default.createInterface({
|
|
46
|
+
input: process.stdin,
|
|
47
|
+
output: process.stdout,
|
|
48
|
+
prompt: chalk_1.default.cyan('> '),
|
|
49
|
+
history: this.history,
|
|
50
|
+
historySize: this.historySize,
|
|
51
|
+
completer: this.completer.bind(this)
|
|
52
|
+
});
|
|
53
|
+
rl.prompt();
|
|
54
|
+
rl.on('line', (line) => {
|
|
55
|
+
// Display bottom horizontal line after input
|
|
56
|
+
console.log(this.createLine());
|
|
57
|
+
rl.close();
|
|
58
|
+
resolve(line);
|
|
59
|
+
});
|
|
60
|
+
// Handle Ctrl+C
|
|
61
|
+
rl.on('SIGINT', () => {
|
|
62
|
+
console.log(this.createLine());
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve('/exit');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Simple tab completer for commands
|
|
70
|
+
*/
|
|
71
|
+
completer(line) {
|
|
72
|
+
const commands = [
|
|
73
|
+
'/help', '/clear', '/exit', '/update', '/config',
|
|
74
|
+
'/init', '/resume', '/skills', '/commands', '/checkpoint',
|
|
75
|
+
'/model', '/use', '/mcp', '/search', '/run', '/commit'
|
|
76
|
+
];
|
|
77
|
+
const hits = commands.filter(c => c.startsWith(line));
|
|
78
|
+
return [hits.length ? hits : commands, line];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Add input to history
|
|
82
|
+
*/
|
|
83
|
+
addToHistory(input) {
|
|
84
|
+
if (!input || input === this.history[0])
|
|
85
|
+
return;
|
|
86
|
+
this.history.unshift(input);
|
|
87
|
+
if (this.history.length > this.historySize) {
|
|
88
|
+
this.history = this.history.slice(0, this.historySize);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get current history
|
|
93
|
+
*/
|
|
94
|
+
getHistory() {
|
|
95
|
+
return this.history;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Display separator and context info before input
|
|
99
|
+
*/
|
|
100
|
+
displayFrame(contextInfo) {
|
|
101
|
+
console.log('');
|
|
102
|
+
// Context bar with message count and percentage
|
|
103
|
+
if (contextInfo) {
|
|
104
|
+
const { messageCount, contextPercent } = contextInfo;
|
|
105
|
+
const color = contextPercent < 60 ? chalk_1.default.green : contextPercent < 80 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
106
|
+
const bar = this.createProgressBar(contextPercent);
|
|
107
|
+
console.log(chalk_1.default.dim(` ${bar} ${messageCount} msgs ${color(contextPercent + '%')}`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Display separator (alias for displayFrame)
|
|
112
|
+
*/
|
|
113
|
+
displaySeparator(contextInfo) {
|
|
114
|
+
this.displayFrame(contextInfo);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create a visual progress bar
|
|
118
|
+
*/
|
|
119
|
+
createProgressBar(percentage) {
|
|
120
|
+
const width = 15;
|
|
121
|
+
const filled = Math.round(percentage / 100 * width);
|
|
122
|
+
const empty = width - filled;
|
|
123
|
+
const color = percentage < 60 ? chalk_1.default.green : percentage < 80 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
124
|
+
return color('█'.repeat(filled)) + chalk_1.default.dim('░'.repeat(empty));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.InputBox = InputBox;
|
package/dist/ui/UIManager.js
CHANGED
|
@@ -19,12 +19,12 @@ class UIManager {
|
|
|
19
19
|
whitespaceBreak: true,
|
|
20
20
|
});
|
|
21
21
|
console.log(gradient_string_1.default.pastel.multiline(logoText));
|
|
22
|
-
console.log(chalk_1.default.gray(' v1.1.
|
|
22
|
+
console.log(chalk_1.default.gray(' v1.1.3 - AI Coding Agent'));
|
|
23
23
|
console.log('');
|
|
24
24
|
}
|
|
25
25
|
static renderDashboard(config) {
|
|
26
26
|
const { model, cwd } = config;
|
|
27
|
-
const version = 'v1.1.
|
|
27
|
+
const version = 'v1.1.3';
|
|
28
28
|
// Layout: Left (Status/Welcome) | Right (Tips/Activity)
|
|
29
29
|
// Total width ~80 chars.
|
|
30
30
|
// Left ~45, Right ~30.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indiccoder/mentis-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"axios": "^1.13.2",
|
|
34
34
|
"boxen": "^8.0.1",
|
|
35
35
|
"chalk": "^5.6.2",
|
|
36
|
+
"cli-cursor": "^5.0.0",
|
|
36
37
|
"cli-highlight": "^2.1.11",
|
|
37
38
|
"commander": "^14.0.2",
|
|
38
39
|
"dotenv": "^17.2.3",
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
"yaml": "^2.7.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
58
|
+
"@types/cli-cursor": "^2.1.0",
|
|
57
59
|
"@types/figlet": "^1.7.0",
|
|
58
60
|
"@types/fs-extra": "^11.0.4",
|
|
59
61
|
"@types/gradient-string": "^1.1.6",
|
package/src/repl/ReplManager.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { OpenAIClient } from '../llm/OpenAIClient';
|
|
|
7
7
|
|
|
8
8
|
import { ContextManager } from '../context/ContextManager';
|
|
9
9
|
import { UIManager } from '../ui/UIManager';
|
|
10
|
+
import { InputBox } from '../ui/InputBox';
|
|
10
11
|
import { WriteFileTool, ReadFileTool, ListDirTool } from '../tools/FileTools';
|
|
11
12
|
import { SearchFileTool } from '../tools/SearchTools';
|
|
12
13
|
import { PersistentShellTool } from '../tools/PersistentShellTool';
|
|
@@ -229,53 +230,38 @@ export class ReplManager {
|
|
|
229
230
|
} catch (e) { }
|
|
230
231
|
}
|
|
231
232
|
|
|
233
|
+
// Initialize InputBox with history
|
|
234
|
+
const inputBox = new InputBox(commandHistory);
|
|
235
|
+
|
|
232
236
|
while (true) {
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// Hint (Claude style puts it below, we put it above for standard terminal compatibility)
|
|
237
|
-
console.log(chalk.dim(' ? for shortcuts'));
|
|
238
|
-
|
|
239
|
-
const promptText = `> `; // Clean prompt
|
|
240
|
-
|
|
241
|
-
// Use readline for basic input to support history
|
|
242
|
-
const answer = await new Promise<string>((resolve) => {
|
|
243
|
-
const rl = readline.createInterface({
|
|
244
|
-
input: process.stdin,
|
|
245
|
-
output: process.stdout,
|
|
246
|
-
history: commandHistory,
|
|
247
|
-
historySize: 1000,
|
|
248
|
-
prompt: promptText
|
|
249
|
-
});
|
|
237
|
+
// Calculate context usage for display
|
|
238
|
+
const usage = this.contextVisualizer.calculateUsage(this.history);
|
|
250
239
|
|
|
251
|
-
|
|
240
|
+
// Display enhanced input frame
|
|
241
|
+
inputBox.displayFrame({
|
|
242
|
+
messageCount: this.history.length,
|
|
243
|
+
contextPercent: usage.percentage
|
|
244
|
+
});
|
|
252
245
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
246
|
+
// Get styled input
|
|
247
|
+
const answer = await inputBox.prompt({
|
|
248
|
+
showHint: this.history.length === 0,
|
|
249
|
+
hint: 'Type your message or /help for commands'
|
|
257
250
|
});
|
|
258
251
|
|
|
259
|
-
// Update history manually or grab from rl?
|
|
260
|
-
// rl.history gets updated when user hits enter.
|
|
261
|
-
// But we closed rl. We should manually save the input to our tracking array and file.
|
|
262
252
|
const input = answer.trim();
|
|
263
253
|
|
|
264
254
|
if (input) {
|
|
265
|
-
// Update
|
|
266
|
-
|
|
267
|
-
// Avoid duplicates if needed, but standard shell keeps them.
|
|
268
|
-
if (commandHistory[0] !== input) {
|
|
269
|
-
commandHistory.unshift(input);
|
|
270
|
-
}
|
|
255
|
+
// Update history via InputBox
|
|
256
|
+
inputBox.addToHistory(input);
|
|
271
257
|
|
|
272
|
-
// Append to file
|
|
258
|
+
// Append to file
|
|
273
259
|
try {
|
|
274
260
|
fs.appendFileSync(HISTORY_FILE, input + '\n');
|
|
275
261
|
} catch (e) { }
|
|
276
262
|
}
|
|
277
263
|
|
|
278
|
-
if (!
|
|
264
|
+
if (!input) continue;
|
|
279
265
|
|
|
280
266
|
if (input.startsWith('/')) {
|
|
281
267
|
await this.handleCommand(input);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InputBox - Simple clean input with top line only
|
|
3
|
+
* Bottom line appears after submission (cross-platform compatible)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
export interface InputBoxOptions {
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
showHint?: boolean;
|
|
12
|
+
hint?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class InputBox {
|
|
16
|
+
private history: string[] = [];
|
|
17
|
+
private historySize: number = 1000;
|
|
18
|
+
|
|
19
|
+
constructor(history: string[] = []) {
|
|
20
|
+
this.history = history;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get terminal width
|
|
25
|
+
*/
|
|
26
|
+
private getTerminalWidth(): number {
|
|
27
|
+
return process.stdout.columns || 80;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create horizontal line
|
|
32
|
+
*/
|
|
33
|
+
private createLine(): string {
|
|
34
|
+
const width = this.getTerminalWidth();
|
|
35
|
+
return chalk.gray('─'.repeat(width));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get user input with horizontal lines around it
|
|
40
|
+
*/
|
|
41
|
+
async prompt(options: InputBoxOptions = {}): Promise<string> {
|
|
42
|
+
const { showHint = false, hint } = options;
|
|
43
|
+
|
|
44
|
+
// Display top horizontal line
|
|
45
|
+
console.log(this.createLine());
|
|
46
|
+
|
|
47
|
+
// Display hint if provided
|
|
48
|
+
if (showHint && hint) {
|
|
49
|
+
console.log(chalk.dim(` ${hint}`));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new Promise<string>((resolve) => {
|
|
53
|
+
// Create readline with simple prompt
|
|
54
|
+
const rl = readline.createInterface({
|
|
55
|
+
input: process.stdin,
|
|
56
|
+
output: process.stdout,
|
|
57
|
+
prompt: chalk.cyan('> '),
|
|
58
|
+
history: this.history,
|
|
59
|
+
historySize: this.historySize,
|
|
60
|
+
completer: this.completer.bind(this)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
rl.prompt();
|
|
64
|
+
|
|
65
|
+
rl.on('line', (line) => {
|
|
66
|
+
// Display bottom horizontal line after input
|
|
67
|
+
console.log(this.createLine());
|
|
68
|
+
rl.close();
|
|
69
|
+
resolve(line);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Handle Ctrl+C
|
|
73
|
+
rl.on('SIGINT', () => {
|
|
74
|
+
console.log(this.createLine());
|
|
75
|
+
rl.close();
|
|
76
|
+
resolve('/exit');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Simple tab completer for commands
|
|
83
|
+
*/
|
|
84
|
+
private completer(line: string) {
|
|
85
|
+
const commands = [
|
|
86
|
+
'/help', '/clear', '/exit', '/update', '/config',
|
|
87
|
+
'/init', '/resume', '/skills', '/commands', '/checkpoint',
|
|
88
|
+
'/model', '/use', '/mcp', '/search', '/run', '/commit'
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const hits = commands.filter(c => c.startsWith(line));
|
|
92
|
+
return [hits.length ? hits : commands, line];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Add input to history
|
|
97
|
+
*/
|
|
98
|
+
addToHistory(input: string): void {
|
|
99
|
+
if (!input || input === this.history[0]) return;
|
|
100
|
+
this.history.unshift(input);
|
|
101
|
+
if (this.history.length > this.historySize) {
|
|
102
|
+
this.history = this.history.slice(0, this.historySize);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get current history
|
|
108
|
+
*/
|
|
109
|
+
getHistory(): string[] {
|
|
110
|
+
return this.history;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Display separator and context info before input
|
|
115
|
+
*/
|
|
116
|
+
public displayFrame(contextInfo?: { messageCount: number; contextPercent: number }): void {
|
|
117
|
+
console.log('');
|
|
118
|
+
|
|
119
|
+
// Context bar with message count and percentage
|
|
120
|
+
if (contextInfo) {
|
|
121
|
+
const { messageCount, contextPercent } = contextInfo;
|
|
122
|
+
const color = contextPercent < 60 ? chalk.green : contextPercent < 80 ? chalk.yellow : chalk.red;
|
|
123
|
+
const bar = this.createProgressBar(contextPercent);
|
|
124
|
+
console.log(chalk.dim(` ${bar} ${messageCount} msgs ${color(contextPercent + '%')}`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Display separator (alias for displayFrame)
|
|
130
|
+
*/
|
|
131
|
+
public displaySeparator(contextInfo?: { messageCount: number; contextPercent: number }): void {
|
|
132
|
+
this.displayFrame(contextInfo);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create a visual progress bar
|
|
137
|
+
*/
|
|
138
|
+
private createProgressBar(percentage: number): string {
|
|
139
|
+
const width = 15;
|
|
140
|
+
const filled = Math.round(percentage / 100 * width);
|
|
141
|
+
const empty = width - filled;
|
|
142
|
+
const color = percentage < 60 ? chalk.green : percentage < 80 ? chalk.yellow : chalk.red;
|
|
143
|
+
return color('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
|
|
144
|
+
}
|
|
145
|
+
}
|
package/src/ui/UIManager.ts
CHANGED
|
@@ -14,13 +14,13 @@ export class UIManager {
|
|
|
14
14
|
whitespaceBreak: true,
|
|
15
15
|
});
|
|
16
16
|
console.log(gradient.pastel.multiline(logoText));
|
|
17
|
-
console.log(chalk.gray(' v1.1.
|
|
17
|
+
console.log(chalk.gray(' v1.1.3 - AI Coding Agent'));
|
|
18
18
|
console.log('');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
public static renderDashboard(config: { model: string, mode: string, cwd: string }) {
|
|
22
22
|
const { model, cwd } = config;
|
|
23
|
-
const version = 'v1.1.
|
|
23
|
+
const version = 'v1.1.3';
|
|
24
24
|
|
|
25
25
|
// Layout: Left (Status/Welcome) | Right (Tips/Activity)
|
|
26
26
|
// Total width ~80 chars.
|