@indiccoder/mentis-cli 1.1.4 → 1.1.5
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/.claude/settings.local.json +8 -0
- package/.mentis/session.json +15 -0
- package/.mentis/sessions/1769189035730.json +23 -0
- package/.mentis/sessions/1769189569160.json +23 -0
- package/.mentis/sessions/1769767538672.json +23 -0
- package/.mentis/sessions/1769767785155.json +23 -0
- package/.mentis/sessions/1769768745802.json +23 -0
- package/.mentis/sessions/1769769600884.json +31 -0
- package/.mentis/sessions/1769770030160.json +31 -0
- package/.mentis/sessions/1769770606004.json +78 -0
- package/.mentis/sessions/1769771084515.json +141 -0
- package/.mentis/sessions/1769881926630.json +57 -0
- package/README.md +17 -0
- package/dist/checkpoint/CheckpointManager.js +92 -0
- package/dist/debug_google.js +61 -0
- package/dist/debug_lite.js +49 -0
- package/dist/debug_lite_headers.js +57 -0
- package/dist/debug_search.js +16 -0
- package/dist/index.js +10 -0
- package/dist/mcp/JsonRpcClient.js +16 -0
- package/dist/mcp/McpConfig.js +132 -0
- package/dist/mcp/McpManager.js +189 -0
- package/dist/repl/PersistentShell.js +20 -1
- package/dist/repl/ReplManager.js +410 -138
- package/dist/tools/AskQuestionTool.js +172 -0
- package/dist/tools/EditFileTool.js +141 -0
- package/dist/tools/FileTools.js +7 -1
- package/dist/tools/PlanModeTool.js +53 -0
- package/dist/tools/WebSearchTool.js +190 -27
- package/dist/ui/DiffViewer.js +110 -0
- package/dist/ui/InputBox.js +16 -2
- package/dist/ui/MultiFileSelector.js +123 -0
- package/dist/ui/PlanModeUI.js +105 -0
- package/dist/ui/ToolExecutor.js +154 -0
- package/dist/ui/UIManager.js +12 -2
- package/docs/MCP_INTEGRATION.md +290 -0
- package/google_dump.html +18 -0
- package/lite_dump.html +176 -0
- package/lite_headers_dump.html +176 -0
- package/package.json +16 -5
- package/scripts/test_exa_mcp.ts +90 -0
- package/src/checkpoint/CheckpointManager.ts +102 -0
- package/src/debug_google.ts +30 -0
- package/src/debug_lite.ts +18 -0
- package/src/debug_lite_headers.ts +25 -0
- package/src/debug_search.ts +18 -0
- package/src/index.ts +12 -0
- package/src/mcp/JsonRpcClient.ts +19 -0
- package/src/mcp/McpConfig.ts +153 -0
- package/src/mcp/McpManager.ts +224 -0
- package/src/repl/PersistentShell.ts +24 -1
- package/src/repl/ReplManager.ts +1521 -1204
- package/src/tools/AskQuestionTool.ts +197 -0
- package/src/tools/EditFileTool.ts +172 -0
- package/src/tools/FileTools.ts +3 -0
- package/src/tools/PlanModeTool.ts +50 -0
- package/src/tools/WebSearchTool.ts +235 -63
- package/src/ui/DiffViewer.ts +117 -0
- package/src/ui/InputBox.ts +17 -2
- package/src/ui/MultiFileSelector.ts +135 -0
- package/src/ui/PlanModeUI.ts +121 -0
- package/src/ui/ToolExecutor.ts +182 -0
- package/src/ui/UIManager.ts +15 -2
- package/console.log(tick) +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DiffViewer = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const diff_1 = require("diff");
|
|
9
|
+
/**
|
|
10
|
+
* Visual diff viewer component
|
|
11
|
+
* Shows file changes with color coding like git diff
|
|
12
|
+
*/
|
|
13
|
+
class DiffViewer {
|
|
14
|
+
/**
|
|
15
|
+
* Display a unified diff between old and new content
|
|
16
|
+
*/
|
|
17
|
+
static showDiff(filePath, oldContent, newContent, contextLines = 3) {
|
|
18
|
+
const diff = (0, diff_1.diffLines)(oldContent, newContent);
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
21
|
+
console.log(chalk_1.default.cyan(`📝 Diff: ${filePath}`));
|
|
22
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
23
|
+
console.log('');
|
|
24
|
+
let unchangedCount = 0;
|
|
25
|
+
const unchangedBuffer = [];
|
|
26
|
+
const flushUnchanged = () => {
|
|
27
|
+
if (unchangedBuffer.length > 0) {
|
|
28
|
+
// Show context lines
|
|
29
|
+
const contextStart = Math.max(0, unchangedBuffer.length - contextLines);
|
|
30
|
+
for (let i = contextStart; i < unchangedBuffer.length; i++) {
|
|
31
|
+
console.log(chalk_1.default.dim(' ' + unchangedBuffer[i].replace(/\n/g, '')));
|
|
32
|
+
}
|
|
33
|
+
unchangedBuffer.length = 0;
|
|
34
|
+
unchangedCount = 0;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
let hasChanges = false;
|
|
38
|
+
let additions = 0;
|
|
39
|
+
let deletions = 0;
|
|
40
|
+
for (const part of diff) {
|
|
41
|
+
const lines = part.value.split('\n');
|
|
42
|
+
// Remove empty last line if exists
|
|
43
|
+
if (lines[lines.length - 1] === '') {
|
|
44
|
+
lines.pop();
|
|
45
|
+
}
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
if (part.added) {
|
|
48
|
+
flushUnchanged();
|
|
49
|
+
console.log(chalk_1.default.green('+ ' + line));
|
|
50
|
+
additions++;
|
|
51
|
+
hasChanges = true;
|
|
52
|
+
}
|
|
53
|
+
else if (part.removed) {
|
|
54
|
+
flushUnchanged();
|
|
55
|
+
console.log(chalk_1.default.red('- ' + line));
|
|
56
|
+
deletions++;
|
|
57
|
+
hasChanges = true;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
unchangedBuffer.push(line);
|
|
61
|
+
unchangedCount++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
flushUnchanged();
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
68
|
+
if (hasChanges) {
|
|
69
|
+
console.log(chalk_1.default.green(`+ ${additions} additions`) + chalk_1.default.dim(' | ') + chalk_1.default.red(`- ${deletions} deletions`));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(chalk_1.default.dim('No changes'));
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Display a simple edit preview (for EditFileTool)
|
|
79
|
+
*/
|
|
80
|
+
static showEditPreview(filePath, oldString, newString, lineNumber) {
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
83
|
+
console.log(chalk_1.default.cyan(`📝 Edit Preview: ${filePath}`));
|
|
84
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
85
|
+
console.log(chalk_1.default.dim(`Line ${lineNumber}:`));
|
|
86
|
+
console.log('');
|
|
87
|
+
const oldLines = oldString.split('\n');
|
|
88
|
+
const newLines = newString.split('\n');
|
|
89
|
+
// Show removed lines in red
|
|
90
|
+
for (const line of oldLines) {
|
|
91
|
+
console.log(chalk_1.default.red('- ' + line));
|
|
92
|
+
}
|
|
93
|
+
// Show added lines in green
|
|
94
|
+
for (const line of newLines) {
|
|
95
|
+
console.log(chalk_1.default.green('+ ' + line));
|
|
96
|
+
}
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Display approval prompt
|
|
103
|
+
*/
|
|
104
|
+
static showApprovalPrompt(filePath, operation) {
|
|
105
|
+
const icon = operation === 'write' ? '📄' : '✏️';
|
|
106
|
+
console.log(chalk_1.default.yellow(`${icon} Approve ${operation} to ${filePath}?`));
|
|
107
|
+
console.log(chalk_1.default.dim(' [y] Yes [n] No [e] Edit'));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.DiffViewer = DiffViewer;
|
package/dist/ui/InputBox.js
CHANGED
|
@@ -51,18 +51,32 @@ class InputBox {
|
|
|
51
51
|
completer: this.completer.bind(this)
|
|
52
52
|
});
|
|
53
53
|
rl.prompt();
|
|
54
|
+
const cleanup = () => {
|
|
55
|
+
rl.close();
|
|
56
|
+
rl.removeAllListeners();
|
|
57
|
+
// Explicitly ensure raw mode is off to prevent terminal freeze
|
|
58
|
+
if (process.stdin.isTTY) {
|
|
59
|
+
process.stdin.setRawMode(false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
54
62
|
rl.on('line', (line) => {
|
|
55
63
|
// Display bottom horizontal line after input
|
|
56
64
|
console.log(this.createLine());
|
|
57
|
-
|
|
65
|
+
cleanup();
|
|
58
66
|
resolve(line);
|
|
59
67
|
});
|
|
60
68
|
// Handle Ctrl+C
|
|
61
69
|
rl.on('SIGINT', () => {
|
|
62
70
|
console.log(this.createLine());
|
|
63
|
-
|
|
71
|
+
cleanup();
|
|
64
72
|
resolve('/exit');
|
|
65
73
|
});
|
|
74
|
+
// Handle stream errors or unexpected close
|
|
75
|
+
rl.on('close', () => {
|
|
76
|
+
// Should already be cleaned up, but safe to ensure
|
|
77
|
+
if (process.stdin.isTTY)
|
|
78
|
+
process.stdin.setRawMode(false);
|
|
79
|
+
});
|
|
66
80
|
});
|
|
67
81
|
}
|
|
68
82
|
/**
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MultiFileSelector = void 0;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
/**
|
|
11
|
+
* Multi-file selector for read approval
|
|
12
|
+
* Shows interactive checklist when AI wants to read multiple files
|
|
13
|
+
*/
|
|
14
|
+
class MultiFileSelector {
|
|
15
|
+
/**
|
|
16
|
+
* Show file selection UI for read operations
|
|
17
|
+
* Returns the list of approved files
|
|
18
|
+
*/
|
|
19
|
+
static async selectFiles(filePaths, message = 'Select files to read:') {
|
|
20
|
+
if (filePaths.length === 0) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
if (filePaths.length === 1) {
|
|
24
|
+
// Single file - just show what's being read
|
|
25
|
+
console.log(chalk_1.default.dim(`📖 Reading: ${filePaths[0]}`));
|
|
26
|
+
return filePaths;
|
|
27
|
+
}
|
|
28
|
+
// Build file choices with metadata
|
|
29
|
+
const choices = filePaths.map(path => {
|
|
30
|
+
let metadata = '';
|
|
31
|
+
try {
|
|
32
|
+
const stats = (0, fs_1.statSync)(path);
|
|
33
|
+
const size = this.formatFileSize(stats.size);
|
|
34
|
+
metadata = chalk_1.default.dim(` (${size})`);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// File might not exist or be inaccessible
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
name: path + metadata,
|
|
41
|
+
value: path,
|
|
42
|
+
checked: true, // Default to checked
|
|
43
|
+
short: path
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log(chalk_1.default.cyan(`📖 AI wants to read ${filePaths.length} files:`));
|
|
48
|
+
console.log('');
|
|
49
|
+
const { selectedFiles } = await inquirer_1.default.prompt([
|
|
50
|
+
{
|
|
51
|
+
type: 'checkbox',
|
|
52
|
+
name: 'selectedFiles',
|
|
53
|
+
message: message,
|
|
54
|
+
choices: choices,
|
|
55
|
+
pageSize: 15,
|
|
56
|
+
validate: (answer) => {
|
|
57
|
+
if (answer.length === 0) {
|
|
58
|
+
return 'You must select at least one file, or press Ctrl+C to cancel.';
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
// Show what was selected
|
|
65
|
+
if (selectedFiles.length < filePaths.length) {
|
|
66
|
+
console.log(chalk_1.default.dim(` Reading ${selectedFiles.length} of ${filePaths.length} files`));
|
|
67
|
+
}
|
|
68
|
+
return selectedFiles;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Show a simple confirmation for single file reads (optional)
|
|
72
|
+
*/
|
|
73
|
+
static async confirmRead(filePath, preview) {
|
|
74
|
+
let message = chalk_1.default.cyan(`📖 Read file: ${filePath}?`);
|
|
75
|
+
if (preview) {
|
|
76
|
+
const lines = preview.split('\n');
|
|
77
|
+
const previewLines = lines.slice(0, 5);
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
80
|
+
console.log(message);
|
|
81
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
82
|
+
console.log(chalk_1.default.dim('Preview:'));
|
|
83
|
+
for (const line of previewLines) {
|
|
84
|
+
console.log(chalk_1.default.dim(' ' + line));
|
|
85
|
+
}
|
|
86
|
+
if (lines.length > 5) {
|
|
87
|
+
console.log(chalk_1.default.dim(' ...'));
|
|
88
|
+
}
|
|
89
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(message);
|
|
93
|
+
}
|
|
94
|
+
const { confirmed } = await inquirer_1.default.prompt([
|
|
95
|
+
{
|
|
96
|
+
type: 'confirm',
|
|
97
|
+
name: 'confirmed',
|
|
98
|
+
message: 'Continue?',
|
|
99
|
+
default: true
|
|
100
|
+
}
|
|
101
|
+
]);
|
|
102
|
+
return confirmed;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Format file size in human-readable format
|
|
106
|
+
*/
|
|
107
|
+
static formatFileSize(bytes) {
|
|
108
|
+
if (bytes === 0)
|
|
109
|
+
return '0 B';
|
|
110
|
+
const k = 1024;
|
|
111
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
112
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
113
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Show progress when reading multiple files
|
|
117
|
+
*/
|
|
118
|
+
static showReadProgress(current, total, filePath) {
|
|
119
|
+
const progress = chalk_1.default.dim(`[${current}/${total}]`);
|
|
120
|
+
console.log(chalk_1.default.dim(` ${progress} Reading: ${filePath}`));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.MultiFileSelector = MultiFileSelector;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PlanModeUI = void 0;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
/**
|
|
10
|
+
* Plan Mode UI - Shows Q&A history and handles plan → build transition
|
|
11
|
+
*/
|
|
12
|
+
class PlanModeUI {
|
|
13
|
+
/**
|
|
14
|
+
* Record a Q&A entry
|
|
15
|
+
*/
|
|
16
|
+
static recordQA(question, answer) {
|
|
17
|
+
this.qaHistory.push({
|
|
18
|
+
question,
|
|
19
|
+
answer,
|
|
20
|
+
timestamp: new Date()
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Show the current Q&A history
|
|
25
|
+
*/
|
|
26
|
+
static showQAHistory() {
|
|
27
|
+
if (this.qaHistory.length === 0) {
|
|
28
|
+
console.log(chalk_1.default.dim(' No questions asked yet.'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(chalk_1.default.cyan('📋 Requirements gathered:'));
|
|
33
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
34
|
+
for (let i = 0; i < this.qaHistory.length; i++) {
|
|
35
|
+
const entry = this.qaHistory[i];
|
|
36
|
+
console.log(chalk_1.default.bold(`${i + 1}. ${entry.question}`));
|
|
37
|
+
console.log(chalk_1.default.dim(` Answer: ${entry.answer}`));
|
|
38
|
+
console.log('');
|
|
39
|
+
}
|
|
40
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Ask if ready to switch to build mode
|
|
44
|
+
*/
|
|
45
|
+
static async askReadyToBuild() {
|
|
46
|
+
console.log('');
|
|
47
|
+
const { ready } = await inquirer_1.default.prompt([
|
|
48
|
+
{
|
|
49
|
+
type: 'confirm',
|
|
50
|
+
name: 'ready',
|
|
51
|
+
message: chalk_1.default.cyan('🚀 Ready to switch to BUILD mode and implement?'),
|
|
52
|
+
default: true
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
return ready;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Show plan mode header/summary
|
|
59
|
+
*/
|
|
60
|
+
static showPlanHeader() {
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(chalk_1.default.cyan.bold('🎯 PLAN MODE'));
|
|
63
|
+
console.log(chalk_1.default.dim(' Gathering requirements and planning the solution...'));
|
|
64
|
+
console.log(chalk_1.default.dim(' Type your requirements, answer questions, then switch to /build to implement.'));
|
|
65
|
+
console.log('');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Show suggestion to switch to build mode
|
|
69
|
+
*/
|
|
70
|
+
static suggestBuildMode() {
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(chalk_1.default.yellow('💡 Tip: Type ') + chalk_1.default.bold('/build') + chalk_1.default.yellow(' to start implementing when ready.'));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Clear Q&A history (e.g., when starting a new session)
|
|
76
|
+
*/
|
|
77
|
+
static clearHistory() {
|
|
78
|
+
this.qaHistory = [];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get Q&A history
|
|
82
|
+
*/
|
|
83
|
+
static getHistory() {
|
|
84
|
+
return [...this.qaHistory];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Show a summary of the plan
|
|
88
|
+
*/
|
|
89
|
+
static showPlanSummary() {
|
|
90
|
+
if (this.qaHistory.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log(chalk_1.default.cyan('📝 Plan Summary:'));
|
|
95
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
96
|
+
console.log(chalk_1.default.dim(`Questions answered: ${this.qaHistory.length}`));
|
|
97
|
+
// Show key answers as bullet points
|
|
98
|
+
for (const entry of this.qaHistory) {
|
|
99
|
+
console.log(chalk_1.default.dim(` • ${entry.question}: ${entry.answer}`));
|
|
100
|
+
}
|
|
101
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.PlanModeUI = PlanModeUI;
|
|
105
|
+
PlanModeUI.qaHistory = [];
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ToolExecutor = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
/**
|
|
10
|
+
* Visual feedback for tool execution
|
|
11
|
+
* Shows colored icons, spinners, and grouped display
|
|
12
|
+
*/
|
|
13
|
+
class ToolExecutor {
|
|
14
|
+
/**
|
|
15
|
+
* Get colored text for a tool name
|
|
16
|
+
*/
|
|
17
|
+
static colorToolName(toolName, color) {
|
|
18
|
+
switch (color) {
|
|
19
|
+
case 'blue': return chalk_1.default.blue(toolName);
|
|
20
|
+
case 'yellow': return chalk_1.default.yellow(toolName);
|
|
21
|
+
case 'cyan': return chalk_1.default.cyan(toolName);
|
|
22
|
+
case 'magenta': return chalk_1.default.magenta(toolName);
|
|
23
|
+
case 'green': return chalk_1.default.green(toolName);
|
|
24
|
+
case 'red': return chalk_1.default.red(toolName);
|
|
25
|
+
case 'gray': return chalk_1.default.gray(toolName);
|
|
26
|
+
default: return toolName;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Start a tool execution with visual feedback
|
|
31
|
+
*/
|
|
32
|
+
static startExecution(toolName, args) {
|
|
33
|
+
const execution = {
|
|
34
|
+
toolName,
|
|
35
|
+
args,
|
|
36
|
+
status: 'running'
|
|
37
|
+
};
|
|
38
|
+
this.executions.push(execution);
|
|
39
|
+
// Get icon and color for tool type
|
|
40
|
+
const { icon, color } = this.getToolStyle(toolName);
|
|
41
|
+
// Format args for display (truncate long values)
|
|
42
|
+
const argsDisplay = this.formatArgs(args);
|
|
43
|
+
// Start spinner
|
|
44
|
+
const spinner = (0, ora_1.default)({
|
|
45
|
+
text: `${icon} ${this.colorToolName(toolName, color)} ${argsDisplay}`,
|
|
46
|
+
color: color
|
|
47
|
+
});
|
|
48
|
+
spinner.start();
|
|
49
|
+
this.spinners.set(toolName, spinner);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Complete a tool execution successfully
|
|
53
|
+
*/
|
|
54
|
+
static completeExecution(toolName, result) {
|
|
55
|
+
const execution = this.executions.find(e => e.toolName === toolName);
|
|
56
|
+
if (execution) {
|
|
57
|
+
execution.status = 'completed';
|
|
58
|
+
execution.result = result;
|
|
59
|
+
}
|
|
60
|
+
const spinner = this.spinners.get(toolName);
|
|
61
|
+
if (spinner) {
|
|
62
|
+
const { icon } = this.getToolStyle(toolName);
|
|
63
|
+
spinner.succeed(`${icon} ${chalk_1.default.green(toolName)} completed`);
|
|
64
|
+
this.spinners.delete(toolName);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Mark a tool execution as failed
|
|
69
|
+
*/
|
|
70
|
+
static failExecution(toolName, error) {
|
|
71
|
+
const execution = this.executions.find(e => e.toolName === toolName);
|
|
72
|
+
if (execution) {
|
|
73
|
+
execution.status = 'failed';
|
|
74
|
+
execution.error = error;
|
|
75
|
+
}
|
|
76
|
+
const spinner = this.spinners.get(toolName);
|
|
77
|
+
if (spinner) {
|
|
78
|
+
const { icon } = this.getToolStyle(toolName);
|
|
79
|
+
spinner.fail(`${icon} ${chalk_1.default.red(toolName)} failed: ${error}`);
|
|
80
|
+
this.spinners.delete(toolName);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Show grouped summary of all tool executions
|
|
85
|
+
*/
|
|
86
|
+
static showSummary() {
|
|
87
|
+
if (this.executions.length === 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
92
|
+
console.log(chalk_1.default.cyan('🔧 Tool Executions'));
|
|
93
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
94
|
+
for (const execution of this.executions) {
|
|
95
|
+
const { icon, color } = this.getToolStyle(execution.toolName);
|
|
96
|
+
const statusIcon = execution.status === 'completed' ? '✓' : execution.status === 'failed' ? '✗' : '…';
|
|
97
|
+
console.log(`${icon} ${this.colorToolName(execution.toolName, color)} ${chalk_1.default.dim(statusIcon)}`);
|
|
98
|
+
}
|
|
99
|
+
console.log(chalk_1.default.gray('─'.repeat(60)));
|
|
100
|
+
console.log('');
|
|
101
|
+
// Reset for next batch
|
|
102
|
+
this.executions = [];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get visual style for a tool type
|
|
106
|
+
*/
|
|
107
|
+
static getToolStyle(toolName) {
|
|
108
|
+
const styles = {
|
|
109
|
+
'read_file': { icon: '📖', color: 'blue' },
|
|
110
|
+
'write_file': { icon: '📄', color: 'yellow' },
|
|
111
|
+
'edit_file': { icon: '✏️', color: 'yellow' },
|
|
112
|
+
'list_dir': { icon: '📁', color: 'cyan' },
|
|
113
|
+
'search_files': { icon: '🔍', color: 'magenta' },
|
|
114
|
+
'search_web': { icon: '🌐', color: 'blue' },
|
|
115
|
+
'run_command': { icon: '💻', color: 'green' },
|
|
116
|
+
'git_commit': { icon: '📝', color: 'green' }
|
|
117
|
+
};
|
|
118
|
+
return styles[toolName] || { icon: '🔧', color: 'gray' };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Format arguments for display
|
|
122
|
+
*/
|
|
123
|
+
static formatArgs(args) {
|
|
124
|
+
const parts = [];
|
|
125
|
+
for (const [key, value] of Object.entries(args)) {
|
|
126
|
+
const strValue = String(value);
|
|
127
|
+
// Truncate long values
|
|
128
|
+
const display = strValue.length > 30 ? strValue.slice(0, 30) + '...' : strValue;
|
|
129
|
+
parts.push(`${key}=${chalk_1.default.dim(display)}`);
|
|
130
|
+
}
|
|
131
|
+
return parts.length > 0 ? chalk_1.default.dim(`(${parts.join(', ')})`) : '';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Show a simple inline tool usage message
|
|
135
|
+
*/
|
|
136
|
+
static showInline(toolName, args) {
|
|
137
|
+
const { icon, color } = this.getToolStyle(toolName);
|
|
138
|
+
const argsDisplay = this.formatArgs(args);
|
|
139
|
+
console.log(chalk_1.default.dim(` ${icon} ${this.colorToolName(toolName, color)} ${argsDisplay}`));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Clear all pending executions
|
|
143
|
+
*/
|
|
144
|
+
static clear() {
|
|
145
|
+
for (const spinner of this.spinners.values()) {
|
|
146
|
+
spinner.stop();
|
|
147
|
+
}
|
|
148
|
+
this.spinners.clear();
|
|
149
|
+
this.executions = [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.ToolExecutor = ToolExecutor;
|
|
153
|
+
ToolExecutor.executions = [];
|
|
154
|
+
ToolExecutor.spinners = new Map();
|
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.6 - 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.6';
|
|
28
28
|
// Layout: Left (Status/Welcome) | Right (Tips/Activity)
|
|
29
29
|
// Total width ~80 chars.
|
|
30
30
|
// Left ~45, Right ~30.
|
|
@@ -74,5 +74,15 @@ class UIManager {
|
|
|
74
74
|
static printSeparator() {
|
|
75
75
|
console.log(chalk_1.default.gray('──────────────────────────────────────────────────'));
|
|
76
76
|
}
|
|
77
|
+
static logBullet(text, color = 'white') {
|
|
78
|
+
const bullet = color === 'white' ? '●' : chalk_1.default[color]('●');
|
|
79
|
+
console.log(` ${bullet} ${text}`);
|
|
80
|
+
}
|
|
81
|
+
static logSystem(text) {
|
|
82
|
+
console.log(chalk_1.default.dim(` ${text}`));
|
|
83
|
+
}
|
|
84
|
+
static logTransition(text) {
|
|
85
|
+
console.log(` ${chalk_1.default.red('+')} ${chalk_1.default.red(text)}`);
|
|
86
|
+
}
|
|
77
87
|
}
|
|
78
88
|
exports.UIManager = UIManager;
|