@indiccoder/mentis-cli 1.1.3 → 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/ARCHITECTURE.md +267 -0
- package/CONTRIBUTING.md +209 -0
- package/README.md +17 -0
- package/dist/checkpoint/CheckpointManager.js +92 -0
- package/dist/commands/Command.js +15 -1
- package/dist/commands/CommandManager.js +30 -5
- package/dist/commands/__tests__/CommandManager.test.js +70 -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 +33 -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/skills/Skill.js +17 -2
- package/dist/skills/SkillsManager.js +28 -3
- package/dist/skills/__tests__/SkillsManager.test.js +62 -0
- 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/dist/utils/__mocks__/chalk.js +20 -0
- package/dist/utils/__tests__/ContextVisualizer.test.js +95 -0
- 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 +34 -2
- package/scripts/test_exa_mcp.ts +90 -0
- package/src/checkpoint/CheckpointManager.ts +102 -0
- package/src/commands/Command.ts +64 -13
- package/src/commands/CommandManager.ts +26 -5
- package/src/commands/__tests__/CommandManager.test.ts +83 -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 +45 -1
- 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/skills/Skill.ts +91 -11
- package/src/skills/SkillsManager.ts +25 -3
- package/src/skills/__tests__/SkillsManager.test.ts +73 -0
- 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/src/utils/__mocks__/chalk.ts +19 -0
- package/src/utils/__tests__/ContextVisualizer.test.ts +118 -0
- package/console.log(tick) +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
export interface QAEntry {
|
|
5
|
+
question: string;
|
|
6
|
+
answer: string;
|
|
7
|
+
timestamp: Date;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Plan Mode UI - Shows Q&A history and handles plan → build transition
|
|
12
|
+
*/
|
|
13
|
+
export class PlanModeUI {
|
|
14
|
+
private static qaHistory: QAEntry[] = [];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Record a Q&A entry
|
|
18
|
+
*/
|
|
19
|
+
static recordQA(question: string, answer: string): void {
|
|
20
|
+
this.qaHistory.push({
|
|
21
|
+
question,
|
|
22
|
+
answer,
|
|
23
|
+
timestamp: new Date()
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Show the current Q&A history
|
|
29
|
+
*/
|
|
30
|
+
static showQAHistory(): void {
|
|
31
|
+
if (this.qaHistory.length === 0) {
|
|
32
|
+
console.log(chalk.dim(' No questions asked yet.'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(chalk.cyan('📋 Requirements gathered:'));
|
|
38
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < this.qaHistory.length; i++) {
|
|
41
|
+
const entry = this.qaHistory[i];
|
|
42
|
+
console.log(chalk.bold(`${i + 1}. ${entry.question}`));
|
|
43
|
+
console.log(chalk.dim(` Answer: ${entry.answer}`));
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Ask if ready to switch to build mode
|
|
52
|
+
*/
|
|
53
|
+
static async askReadyToBuild(): Promise<boolean> {
|
|
54
|
+
console.log('');
|
|
55
|
+
|
|
56
|
+
const { ready } = await inquirer.prompt([
|
|
57
|
+
{
|
|
58
|
+
type: 'confirm',
|
|
59
|
+
name: 'ready',
|
|
60
|
+
message: chalk.cyan('🚀 Ready to switch to BUILD mode and implement?'),
|
|
61
|
+
default: true
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
return ready;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Show plan mode header/summary
|
|
70
|
+
*/
|
|
71
|
+
static showPlanHeader(): void {
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(chalk.cyan.bold('🎯 PLAN MODE'));
|
|
74
|
+
console.log(chalk.dim(' Gathering requirements and planning the solution...'));
|
|
75
|
+
console.log(chalk.dim(' Type your requirements, answer questions, then switch to /build to implement.'));
|
|
76
|
+
console.log('');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Show suggestion to switch to build mode
|
|
81
|
+
*/
|
|
82
|
+
static suggestBuildMode(): void {
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log(chalk.yellow('💡 Tip: Type ') + chalk.bold('/build') + chalk.yellow(' to start implementing when ready.'));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Clear Q&A history (e.g., when starting a new session)
|
|
89
|
+
*/
|
|
90
|
+
static clearHistory(): void {
|
|
91
|
+
this.qaHistory = [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get Q&A history
|
|
96
|
+
*/
|
|
97
|
+
static getHistory(): QAEntry[] {
|
|
98
|
+
return [...this.qaHistory];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Show a summary of the plan
|
|
103
|
+
*/
|
|
104
|
+
static showPlanSummary(): void {
|
|
105
|
+
if (this.qaHistory.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(chalk.cyan('📝 Plan Summary:'));
|
|
111
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
112
|
+
console.log(chalk.dim(`Questions answered: ${this.qaHistory.length}`));
|
|
113
|
+
|
|
114
|
+
// Show key answers as bullet points
|
|
115
|
+
for (const entry of this.qaHistory) {
|
|
116
|
+
console.log(chalk.dim(` • ${entry.question}: ${entry.answer}`));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora, { Ora } from 'ora';
|
|
3
|
+
|
|
4
|
+
export interface ToolExecution {
|
|
5
|
+
toolName: string;
|
|
6
|
+
args: Record<string, any>;
|
|
7
|
+
status: 'running' | 'completed' | 'failed';
|
|
8
|
+
result?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Visual feedback for tool execution
|
|
14
|
+
* Shows colored icons, spinners, and grouped display
|
|
15
|
+
*/
|
|
16
|
+
export class ToolExecutor {
|
|
17
|
+
private static executions: ToolExecution[] = [];
|
|
18
|
+
private static spinners: Map<string, Ora> = new Map();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get colored text for a tool name
|
|
22
|
+
*/
|
|
23
|
+
private static colorToolName(toolName: string, color: string): string {
|
|
24
|
+
switch (color) {
|
|
25
|
+
case 'blue': return chalk.blue(toolName);
|
|
26
|
+
case 'yellow': return chalk.yellow(toolName);
|
|
27
|
+
case 'cyan': return chalk.cyan(toolName);
|
|
28
|
+
case 'magenta': return chalk.magenta(toolName);
|
|
29
|
+
case 'green': return chalk.green(toolName);
|
|
30
|
+
case 'red': return chalk.red(toolName);
|
|
31
|
+
case 'gray': return chalk.gray(toolName);
|
|
32
|
+
default: return toolName;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Start a tool execution with visual feedback
|
|
38
|
+
*/
|
|
39
|
+
static startExecution(toolName: string, args: Record<string, any>): void {
|
|
40
|
+
const execution: ToolExecution = {
|
|
41
|
+
toolName,
|
|
42
|
+
args,
|
|
43
|
+
status: 'running'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
this.executions.push(execution);
|
|
47
|
+
|
|
48
|
+
// Get icon and color for tool type
|
|
49
|
+
const { icon, color } = this.getToolStyle(toolName);
|
|
50
|
+
|
|
51
|
+
// Format args for display (truncate long values)
|
|
52
|
+
const argsDisplay = this.formatArgs(args);
|
|
53
|
+
|
|
54
|
+
// Start spinner
|
|
55
|
+
const spinner = ora({
|
|
56
|
+
text: `${icon} ${this.colorToolName(toolName, color)} ${argsDisplay}`,
|
|
57
|
+
color: color as 'cyan' | 'yellow' | 'red' | 'green' | 'blue' | 'magenta' | 'white' | 'gray'
|
|
58
|
+
});
|
|
59
|
+
spinner.start();
|
|
60
|
+
|
|
61
|
+
this.spinners.set(toolName, spinner);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Complete a tool execution successfully
|
|
66
|
+
*/
|
|
67
|
+
static completeExecution(toolName: string, result: string): void {
|
|
68
|
+
const execution = this.executions.find(e => e.toolName === toolName);
|
|
69
|
+
if (execution) {
|
|
70
|
+
execution.status = 'completed';
|
|
71
|
+
execution.result = result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const spinner = this.spinners.get(toolName);
|
|
75
|
+
if (spinner) {
|
|
76
|
+
const { icon } = this.getToolStyle(toolName);
|
|
77
|
+
spinner.succeed(`${icon} ${chalk.green(toolName)} completed`);
|
|
78
|
+
this.spinners.delete(toolName);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Mark a tool execution as failed
|
|
84
|
+
*/
|
|
85
|
+
static failExecution(toolName: string, error: string): void {
|
|
86
|
+
const execution = this.executions.find(e => e.toolName === toolName);
|
|
87
|
+
if (execution) {
|
|
88
|
+
execution.status = 'failed';
|
|
89
|
+
execution.error = error;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const spinner = this.spinners.get(toolName);
|
|
93
|
+
if (spinner) {
|
|
94
|
+
const { icon } = this.getToolStyle(toolName);
|
|
95
|
+
spinner.fail(`${icon} ${chalk.red(toolName)} failed: ${error}`);
|
|
96
|
+
this.spinners.delete(toolName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Show grouped summary of all tool executions
|
|
102
|
+
*/
|
|
103
|
+
static showSummary(): void {
|
|
104
|
+
if (this.executions.length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
110
|
+
console.log(chalk.cyan('🔧 Tool Executions'));
|
|
111
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
112
|
+
|
|
113
|
+
for (const execution of this.executions) {
|
|
114
|
+
const { icon, color } = this.getToolStyle(execution.toolName);
|
|
115
|
+
const statusIcon = execution.status === 'completed' ? '✓' : execution.status === 'failed' ? '✗' : '…';
|
|
116
|
+
|
|
117
|
+
console.log(
|
|
118
|
+
`${icon} ${this.colorToolName(execution.toolName, color)} ${chalk.dim(statusIcon)}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
123
|
+
console.log('');
|
|
124
|
+
|
|
125
|
+
// Reset for next batch
|
|
126
|
+
this.executions = [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get visual style for a tool type
|
|
131
|
+
*/
|
|
132
|
+
private static getToolStyle(toolName: string): { icon: string; color: string } {
|
|
133
|
+
const styles: Record<string, { icon: string; color: string }> = {
|
|
134
|
+
'read_file': { icon: '📖', color: 'blue' },
|
|
135
|
+
'write_file': { icon: '📄', color: 'yellow' },
|
|
136
|
+
'edit_file': { icon: '✏️', color: 'yellow' },
|
|
137
|
+
'list_dir': { icon: '📁', color: 'cyan' },
|
|
138
|
+
'search_files': { icon: '🔍', color: 'magenta' },
|
|
139
|
+
'search_web': { icon: '🌐', color: 'blue' },
|
|
140
|
+
'run_command': { icon: '💻', color: 'green' },
|
|
141
|
+
'git_commit': { icon: '📝', color: 'green' }
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return styles[toolName] || { icon: '🔧', color: 'gray' };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Format arguments for display
|
|
149
|
+
*/
|
|
150
|
+
private static formatArgs(args: Record<string, any>): string {
|
|
151
|
+
const parts: string[] = [];
|
|
152
|
+
|
|
153
|
+
for (const [key, value] of Object.entries(args)) {
|
|
154
|
+
const strValue = String(value);
|
|
155
|
+
// Truncate long values
|
|
156
|
+
const display = strValue.length > 30 ? strValue.slice(0, 30) + '...' : strValue;
|
|
157
|
+
parts.push(`${key}=${chalk.dim(display)}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return parts.length > 0 ? chalk.dim(`(${parts.join(', ')})`) : '';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Show a simple inline tool usage message
|
|
165
|
+
*/
|
|
166
|
+
static showInline(toolName: string, args: Record<string, any>): void {
|
|
167
|
+
const { icon, color } = this.getToolStyle(toolName);
|
|
168
|
+
const argsDisplay = this.formatArgs(args);
|
|
169
|
+
console.log(chalk.dim(` ${icon} ${this.colorToolName(toolName, color)} ${argsDisplay}`));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Clear all pending executions
|
|
174
|
+
*/
|
|
175
|
+
static clear(): void {
|
|
176
|
+
for (const spinner of this.spinners.values()) {
|
|
177
|
+
spinner.stop();
|
|
178
|
+
}
|
|
179
|
+
this.spinners.clear();
|
|
180
|
+
this.executions = [];
|
|
181
|
+
}
|
|
182
|
+
}
|
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.6 - 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.6';
|
|
24
24
|
|
|
25
25
|
// Layout: Left (Status/Welcome) | Right (Tips/Activity)
|
|
26
26
|
// Total width ~80 chars.
|
|
@@ -84,4 +84,17 @@ export class UIManager {
|
|
|
84
84
|
public static printSeparator() {
|
|
85
85
|
console.log(chalk.gray('──────────────────────────────────────────────────'));
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
public static logBullet(text: string, color: 'cyan' | 'green' | 'yellow' | 'red' | 'blue' | 'magenta' | 'white' = 'white') {
|
|
89
|
+
const bullet = color === 'white' ? '●' : chalk[color]('●');
|
|
90
|
+
console.log(` ${bullet} ${text}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public static logSystem(text: string) {
|
|
94
|
+
console.log(chalk.dim(` ${text}`));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public static logTransition(text: string) {
|
|
98
|
+
console.log(` ${chalk.red('+')} ${chalk.red(text)}`);
|
|
99
|
+
}
|
|
87
100
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Manual mock for chalk to avoid ESM issues in Jest
|
|
2
|
+
module.exports = {
|
|
3
|
+
dim: (str: string) => str,
|
|
4
|
+
green: (str: string) => str,
|
|
5
|
+
yellow: (str: string) => str,
|
|
6
|
+
red: (str: string) => str,
|
|
7
|
+
gray: (str: string) => str,
|
|
8
|
+
bold: (str: string) => str,
|
|
9
|
+
cyan: (str: string) => str,
|
|
10
|
+
default: {
|
|
11
|
+
dim: (str: string) => str,
|
|
12
|
+
green: (str: string) => str,
|
|
13
|
+
yellow: (str: string) => str,
|
|
14
|
+
red: (str: string) => str,
|
|
15
|
+
gray: (str: string) => str,
|
|
16
|
+
bold: (str: string) => str,
|
|
17
|
+
cyan: (str: string) => str
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for ContextVisualizer
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ContextVisualizer } from '../ContextVisualizer';
|
|
6
|
+
import { ChatMessage } from '../../llm/ModelInterface';
|
|
7
|
+
|
|
8
|
+
describe('ContextVisualizer', () => {
|
|
9
|
+
let visualizer: ContextVisualizer;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
visualizer = new ContextVisualizer();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('calculateUsage', () => {
|
|
16
|
+
it('should handle empty history', () => {
|
|
17
|
+
const history: ChatMessage[] = [];
|
|
18
|
+
const usage = visualizer.calculateUsage(history);
|
|
19
|
+
|
|
20
|
+
// Includes 2000 char overhead = 500 tokens
|
|
21
|
+
expect(usage.tokens).toBe(500);
|
|
22
|
+
expect(usage.maxTokens).toBe(128000);
|
|
23
|
+
expect(usage).toHaveProperty('percentage');
|
|
24
|
+
expect(usage).toHaveProperty('tokens');
|
|
25
|
+
expect(usage).toHaveProperty('maxTokens');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should calculate tokens for messages', () => {
|
|
29
|
+
const history: ChatMessage[] = [
|
|
30
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
31
|
+
{ role: 'user', content: 'Hello' },
|
|
32
|
+
{ role: 'assistant', content: 'Hi there!' }
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const usage = visualizer.calculateUsage(history);
|
|
36
|
+
|
|
37
|
+
expect(usage.tokens).toBeGreaterThan(500);
|
|
38
|
+
expect(usage.tokens).toBeLessThan(1000);
|
|
39
|
+
expect(usage.maxTokens).toBe(128000);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle large messages', () => {
|
|
43
|
+
const largeContent = 'x'.repeat(10000);
|
|
44
|
+
const history: ChatMessage[] = [
|
|
45
|
+
{ role: 'user', content: largeContent }
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const usage = visualizer.calculateUsage(history);
|
|
49
|
+
|
|
50
|
+
expect(usage.tokens).toBeGreaterThan(1500);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('formatBar', () => {
|
|
55
|
+
it('should format bar at low usage', () => {
|
|
56
|
+
const usage = { tokens: 1000, percentage: 5, maxTokens: 128000 };
|
|
57
|
+
const bar = visualizer.formatBar(usage);
|
|
58
|
+
|
|
59
|
+
// Check that bar contains expected data (without chalk dependency)
|
|
60
|
+
expect(bar).toContain('5');
|
|
61
|
+
expect(bar).toContain('1k');
|
|
62
|
+
expect(bar).toContain('128');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should format bar at medium usage', () => {
|
|
66
|
+
const usage = { tokens: 50000, percentage: 40, maxTokens: 128000 };
|
|
67
|
+
const bar = visualizer.formatBar(usage);
|
|
68
|
+
|
|
69
|
+
expect(bar).toContain('40');
|
|
70
|
+
expect(bar).toContain('50k');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should format bar at high usage', () => {
|
|
74
|
+
const usage = { tokens: 100000, percentage: 80, maxTokens: 128000 };
|
|
75
|
+
const bar = visualizer.formatBar(usage);
|
|
76
|
+
|
|
77
|
+
expect(bar).toContain('80');
|
|
78
|
+
expect(bar).toContain('100k');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('shouldCompact', () => {
|
|
83
|
+
it('should return false for low percentage', () => {
|
|
84
|
+
const history: ChatMessage[] = [
|
|
85
|
+
{ role: 'user', content: 'small message' }
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const shouldCompact = visualizer.shouldCompact(history);
|
|
89
|
+
expect(shouldCompact).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should return true at 80% threshold', () => {
|
|
93
|
+
// Create enough content to exceed 80%
|
|
94
|
+
// 80% of 128000 tokens = 102400 tokens = ~409600 chars
|
|
95
|
+
// Subtract 2000 overhead = ~407400 chars needed
|
|
96
|
+
const largeContent = 'x'.repeat(410000);
|
|
97
|
+
const history: ChatMessage[] = [
|
|
98
|
+
{ role: 'system', content: largeContent },
|
|
99
|
+
{ role: 'user', content: largeContent }
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const shouldCompact = visualizer.shouldCompact(history);
|
|
103
|
+
expect(shouldCompact).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('setMaxTokens', () => {
|
|
108
|
+
it('should update max tokens', () => {
|
|
109
|
+
visualizer.setMaxTokens(32000);
|
|
110
|
+
|
|
111
|
+
const history: ChatMessage[] = [];
|
|
112
|
+
const usage = visualizer.calculateUsage(history);
|
|
113
|
+
|
|
114
|
+
expect(usage.maxTokens).toBe(32000);
|
|
115
|
+
expect(usage.percentage).toBeGreaterThan(1); // Should be higher percentage with smaller max
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
package/console.log(tick)
DELETED
|
File without changes
|