@indiccoder/mentis-cli 1.0.9 → 1.1.0

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.
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /**
3
+ * SlashCommandTool - Tool for model-invoked custom command execution
4
+ * The model can call this tool when it determines a custom command should be run
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.ListCommandsTool = exports.SlashCommandTool = void 0;
41
+ const fs = __importStar(require("fs"));
42
+ const child_process_1 = require("child_process");
43
+ const util_1 = require("util");
44
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
45
+ class SlashCommandTool {
46
+ constructor(commandManager) {
47
+ this.name = 'slash_command';
48
+ this.description = 'Execute a custom slash command. Available commands can be seen in /help. Example: slash_command({ name: "review", arguments: ["src/file.ts"] })';
49
+ this.parameters = {
50
+ type: 'object',
51
+ properties: {
52
+ name: {
53
+ type: 'string',
54
+ description: 'The name of the custom command to execute'
55
+ },
56
+ arguments: {
57
+ type: 'array',
58
+ items: { type: 'string' },
59
+ description: 'Arguments to pass to the command'
60
+ }
61
+ },
62
+ required: ['name']
63
+ };
64
+ this.commandManager = commandManager;
65
+ }
66
+ async execute(args) {
67
+ const { name, arguments: cmdArgs = [] } = args;
68
+ if (!name) {
69
+ return 'Error: Command name is required';
70
+ }
71
+ const command = this.commandManager.getCommand(name);
72
+ if (!command) {
73
+ const availableCommands = this.commandManager.getAllCommands().map(c => c.name).join(', ');
74
+ return `Error: Command "${name}" not found. Available commands: ${availableCommands || 'none'}`;
75
+ }
76
+ try {
77
+ // Parse command and handle substitutions
78
+ const parsed = await this.commandManager.parseCommand(command, cmdArgs);
79
+ let content = parsed.content;
80
+ // Execute bash commands and collect results
81
+ const bashResults = [];
82
+ for (const bashCmd of parsed.bashCommands) {
83
+ try {
84
+ const result = await execAsync(bashCmd);
85
+ bashResults.push(`${bashCmd}\n${result.stdout}`);
86
+ }
87
+ catch (error) {
88
+ bashResults.push(`${bashCmd}\nError: ${error.message}`);
89
+ }
90
+ }
91
+ // Substitute bash outputs into content
92
+ let bashIndex = 0;
93
+ content = content.replace(/\[BASH_OUTPUT\]/g, () => {
94
+ return bashResults[bashIndex++] || '';
95
+ });
96
+ // Read file references
97
+ const fileContents = [];
98
+ for (const filePath of parsed.fileReferences) {
99
+ try {
100
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
101
+ fileContents.push(`\n=== File: ${filePath} ===\n${fileContent}\n=== End of ${filePath} ===\n`);
102
+ }
103
+ catch (error) {
104
+ fileContents.push(`\n=== File: ${filePath} ===\nError: ${error.message}\n=== End of ${filePath} ===\n`);
105
+ }
106
+ }
107
+ // Substitute file references
108
+ let fileIndex = 0;
109
+ content = content.replace(/@[^\s]+/g, () => {
110
+ return fileContents[fileIndex++] || '';
111
+ });
112
+ return `# Executing: /${name}${cmdArgs.length ? ' ' + cmdArgs.join(' ') : ''}\n\n${content}`;
113
+ }
114
+ catch (error) {
115
+ return `Error executing command "${name}": ${error.message}`;
116
+ }
117
+ }
118
+ }
119
+ exports.SlashCommandTool = SlashCommandTool;
120
+ /**
121
+ * ListCommandsTool - Tool for listing custom commands
122
+ */
123
+ class ListCommandsTool {
124
+ constructor(commandManager) {
125
+ this.name = 'list_commands';
126
+ this.description = 'List all available custom slash commands with their descriptions';
127
+ this.parameters = {
128
+ type: 'object',
129
+ properties: {},
130
+ required: []
131
+ };
132
+ this.commandManager = commandManager;
133
+ }
134
+ async execute() {
135
+ const commands = this.commandManager.getAllCommands();
136
+ if (commands.length === 0) {
137
+ return 'No custom commands available. Add commands to ~/.mentis/commands/ or .mentis/commands/';
138
+ }
139
+ let response = `# Custom Commands (${commands.length})\n\n`;
140
+ // Group by namespace
141
+ const grouped = new Map();
142
+ for (const cmd of commands) {
143
+ const ns = cmd.description.match(/\(([^)]+)\)/)?.[1] || cmd.type;
144
+ if (!grouped.has(ns)) {
145
+ grouped.set(ns, []);
146
+ }
147
+ grouped.get(ns).push(cmd);
148
+ }
149
+ for (const [namespace, cmds] of grouped) {
150
+ response += `## ${namespace}\n\n`;
151
+ for (const cmd of cmds) {
152
+ const params = cmd.frontmatter['argument-hint'] ? ` ${cmd.frontmatter['argument-hint']}` : '';
153
+ response += `**/${cmd.name}${params}**\n`;
154
+ response += `${cmd.description}\n\n`;
155
+ }
156
+ }
157
+ return response;
158
+ }
159
+ }
160
+ exports.ListCommandsTool = ListCommandsTool;
@@ -54,6 +54,11 @@ const McpClient_1 = require("../mcp/McpClient");
54
54
  const CheckpointManager_1 = require("../checkpoint/CheckpointManager");
55
55
  const SkillsManager_1 = require("../skills/SkillsManager");
56
56
  const LoadSkillTool_1 = require("../skills/LoadSkillTool");
57
+ const ContextVisualizer_1 = require("../utils/ContextVisualizer");
58
+ const ProjectInitializer_1 = require("../utils/ProjectInitializer");
59
+ const ConversationCompacter_1 = require("../utils/ConversationCompacter");
60
+ const CommandManager_1 = require("../commands/CommandManager");
61
+ const SlashCommandTool_1 = require("../commands/SlashCommandTool");
57
62
  const readline = __importStar(require("readline"));
58
63
  const fs = __importStar(require("fs"));
59
64
  const path = __importStar(require("path"));
@@ -73,6 +78,9 @@ class ReplManager {
73
78
  this.contextManager = new ContextManager_1.ContextManager();
74
79
  this.checkpointManager = new CheckpointManager_1.CheckpointManager();
75
80
  this.skillsManager = new SkillsManager_1.SkillsManager();
81
+ this.contextVisualizer = new ContextVisualizer_1.ContextVisualizer();
82
+ this.conversationCompacter = new ConversationCompacter_1.ConversationCompacter();
83
+ this.commandManager = new CommandManager_1.CommandManager();
76
84
  this.shell = new PersistentShell_1.PersistentShell();
77
85
  // Create tools array without skill tools first
78
86
  this.tools = [
@@ -99,16 +107,20 @@ class ReplManager {
99
107
  this.initializeSkills();
100
108
  }
101
109
  /**
102
- * Initialize the skills system
110
+ * Initialize the skills and custom commands system
103
111
  */
104
112
  async initializeSkills() {
113
+ // Initialize skills
105
114
  this.skillsManager.ensureDirectoriesExist();
106
115
  await this.skillsManager.discoverSkills();
116
+ // Initialize custom commands
117
+ this.commandManager.ensureDirectoriesExist();
118
+ await this.commandManager.discoverCommands();
107
119
  // Add skill tools to the tools list
108
120
  // Pass callback to LoadSkillTool to track active skill
109
121
  this.tools.push(new LoadSkillTool_1.LoadSkillTool(this.skillsManager, (skill) => {
110
122
  this.activeSkill = skill ? skill.name : null;
111
- }), new LoadSkillTool_1.ListSkillsTool(this.skillsManager), new LoadSkillTool_1.ReadSkillFileTool(this.skillsManager));
123
+ }), new LoadSkillTool_1.ListSkillsTool(this.skillsManager), new LoadSkillTool_1.ReadSkillFileTool(this.skillsManager), new SlashCommandTool_1.SlashCommandTool(this.commandManager), new SlashCommandTool_1.ListCommandsTool(this.commandManager));
112
124
  }
113
125
  /**
114
126
  * Check if a tool is allowed by the currently active skill
@@ -141,7 +153,9 @@ class ReplManager {
141
153
  'git_pull': 'GitPull',
142
154
  'load_skill': 'Read',
143
155
  'list_skills': 'Read',
144
- 'read_skill_file': 'Read'
156
+ 'read_skill_file': 'Read',
157
+ 'slash_command': 'Read',
158
+ 'list_commands': 'Read'
145
159
  };
146
160
  const mappedToolName = toolMapping[toolName] || toolName;
147
161
  return skill.allowedTools.includes(mappedToolName);
@@ -256,11 +270,13 @@ class ReplManager {
256
270
  console.log(' /use <provider> [model] - Quick switch (legacy)');
257
271
  console.log(' /mcp <cmd> - Manage MCP servers');
258
272
  console.log(' /skills <list|show|create|validate> - Manage Agent Skills');
273
+ console.log(' /commands <list|create|validate> - Manage Custom Commands');
259
274
  console.log(' /resume - Resume last session');
260
275
  console.log(' /checkpoint <save|load|list> [name] - Manage checkpoints');
261
276
  console.log(' /search <query> - Search codebase');
262
277
  console.log(' /run <cmd> - Run shell command');
263
278
  console.log(' /commit [msg] - Git commit all changes');
279
+ console.log(' /init - Initialize project with .mentis.md');
264
280
  break;
265
281
  case '/plan':
266
282
  this.mode = 'PLAN';
@@ -332,9 +348,15 @@ class ReplManager {
332
348
  const updater = new UpdateManager();
333
349
  await updater.checkAndPerformUpdate(true);
334
350
  break;
351
+ case '/init':
352
+ await this.handleInitCommand();
353
+ break;
335
354
  case '/skills':
336
355
  await this.handleSkillsCommand(args);
337
356
  break;
357
+ case '/commands':
358
+ await this.handleCommandsCommand(args);
359
+ break;
338
360
  default:
339
361
  console.log(chalk_1.default.red(`Unknown command: ${command}`));
340
362
  }
@@ -342,6 +364,7 @@ class ReplManager {
342
364
  async handleChat(input) {
343
365
  const context = this.contextManager.getContextString();
344
366
  const skillsContext = this.skillsManager.getSkillsContext();
367
+ const commandsContext = this.commandManager.getCommandsContext();
345
368
  let fullInput = input;
346
369
  let modeInstruction = '';
347
370
  if (this.mode === 'PLAN') {
@@ -355,6 +378,10 @@ class ReplManager {
355
378
  if (skillsContext) {
356
379
  fullInput = `${skillsContext}\n\n${fullInput}`;
357
380
  }
381
+ // Add commands context if available
382
+ if (commandsContext) {
383
+ fullInput = `${commandsContext}\n\n${fullInput}`;
384
+ }
358
385
  if (context) {
359
386
  fullInput = `${context}\n\nUser Question: ${fullInput}`;
360
387
  }
@@ -494,8 +521,16 @@ class ReplManager {
494
521
  const totalCost = this.estimateCost(input_tokens, output_tokens);
495
522
  console.log(chalk_1.default.dim(`\n(Tokens: ${input_tokens} in / ${output_tokens} out | Est. Cost: $${totalCost.toFixed(5)})`));
496
523
  }
524
+ // Display context bar
525
+ const contextBar = this.contextVisualizer.getContextBar(this.history);
526
+ console.log(chalk_1.default.dim(`\n${contextBar}`));
497
527
  console.log('');
498
528
  this.history.push({ role: 'assistant', content: response.content });
529
+ // Auto-compact prompt when context is at 80%
530
+ const usage = this.contextVisualizer.calculateUsage(this.history);
531
+ if (usage.percentage >= 80) {
532
+ this.history = await this.conversationCompacter.promptIfCompactNeeded(usage.percentage, this.history, this.modelClient);
533
+ }
499
534
  }
500
535
  }
501
536
  catch (error) {
@@ -966,6 +1001,74 @@ class ReplManager {
966
1001
  console.log(chalk_1.default.dim(`\nSupporting files: ${files.join(', ')}`));
967
1002
  }
968
1003
  }
1004
+ async handleInitCommand() {
1005
+ const initializer = new ProjectInitializer_1.ProjectInitializer();
1006
+ await initializer.run();
1007
+ }
1008
+ async handleCommandsCommand(args) {
1009
+ if (args.length < 1) {
1010
+ // Show commands list by default
1011
+ await this.handleCommandsCommand(['list']);
1012
+ return;
1013
+ }
1014
+ const action = args[0];
1015
+ switch (action) {
1016
+ case 'list':
1017
+ await this.handleCommandsList();
1018
+ break;
1019
+ case 'create':
1020
+ await this.handleCommandsCreate(args[1]);
1021
+ break;
1022
+ case 'validate':
1023
+ await this.handleCommandsValidate();
1024
+ break;
1025
+ default:
1026
+ console.log(chalk_1.default.red(`Unknown commands action: ${action}`));
1027
+ console.log(chalk_1.default.yellow('Available actions: list, create, validate'));
1028
+ }
1029
+ }
1030
+ async handleCommandsList() {
1031
+ const commands = this.commandManager.getAllCommands();
1032
+ if (commands.length === 0) {
1033
+ console.log(chalk_1.default.yellow('No custom commands available.'));
1034
+ console.log(chalk_1.default.dim('Create commands with: /commands create'));
1035
+ console.log(chalk_1.default.dim('Add commands to: ~/.mentis/commands/ or .mentis/commands/'));
1036
+ return;
1037
+ }
1038
+ console.log(chalk_1.default.cyan(`\nCustom Commands (${commands.length}):\n`));
1039
+ // Group by namespace
1040
+ const grouped = new Map();
1041
+ for (const cmd of commands) {
1042
+ const ns = cmd.description.match(/\(([^)]+)\)/)?.[1] || cmd.type;
1043
+ if (!grouped.has(ns)) {
1044
+ grouped.set(ns, []);
1045
+ }
1046
+ grouped.get(ns).push(cmd);
1047
+ }
1048
+ for (const [namespace, cmds] of grouped) {
1049
+ console.log(chalk_1.default.bold(`\n${namespace}`));
1050
+ for (const cmd of cmds) {
1051
+ const params = cmd.frontmatter['argument-hint'] ? ` ${cmd.frontmatter['argument-hint']}` : '';
1052
+ console.log(` /${cmd.name}${params}`);
1053
+ console.log(` ${cmd.description.replace(/\s*\([^)]+\)/, '')}`);
1054
+ if (cmd.frontmatter['allowed-tools'] && cmd.frontmatter['allowed-tools'].length > 0) {
1055
+ console.log(chalk_1.default.dim(` Allowed tools: ${cmd.frontmatter['allowed-tools'].join(', ')}`));
1056
+ }
1057
+ }
1058
+ }
1059
+ console.log('');
1060
+ }
1061
+ async handleCommandsCreate(name) {
1062
+ const { CommandCreator } = await Promise.resolve().then(() => __importStar(require('../commands/CommandCreator')));
1063
+ const creator = new CommandCreator(this.commandManager);
1064
+ await creator.run(name);
1065
+ // Re-discover commands after creation
1066
+ await this.commandManager.discoverCommands();
1067
+ }
1068
+ async handleCommandsValidate() {
1069
+ const { validateCommands } = await Promise.resolve().then(() => __importStar(require('../commands/CommandCreator')));
1070
+ await validateCommands(this.commandManager);
1071
+ }
969
1072
  estimateCost(input, output) {
970
1073
  const config = this.configManager.getConfig();
971
1074
  const provider = config.defaultProvider;
@@ -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.0.5 - AI Coding Agent'));
22
+ console.log(chalk_1.default.gray(' v1.1.0 - AI Coding Agent'));
23
23
  console.log('');
24
24
  }
25
25
  static renderDashboard(config) {
26
26
  const { model, cwd } = config;
27
- const version = 'v1.0.8';
27
+ const version = 'v1.1.0';
28
28
  // Layout: Left (Status/Welcome) | Right (Tips/Activity)
29
29
  // Total width ~80 chars.
30
30
  // Left ~45, Right ~30.
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * ContextVisualizer - Auto context bar display
4
+ * Shows token usage as a colored progress bar
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.ContextVisualizer = void 0;
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ class ContextVisualizer {
13
+ constructor(maxTokens) {
14
+ this.maxTokens = 128000; // Default context window
15
+ if (maxTokens) {
16
+ this.maxTokens = maxTokens;
17
+ }
18
+ }
19
+ /**
20
+ * Calculate approximate token count from text
21
+ * Rough estimate: ~4 characters per token
22
+ */
23
+ estimateTokens(text) {
24
+ if (!text)
25
+ return 0;
26
+ // Rough estimation: ~4 characters per token for English text
27
+ return Math.ceil(text.length / 4);
28
+ }
29
+ /**
30
+ * Calculate total tokens from message history
31
+ */
32
+ calculateUsage(history) {
33
+ let totalChars = 0;
34
+ for (const msg of history) {
35
+ if (msg.content) {
36
+ totalChars += msg.content.length;
37
+ }
38
+ if (msg.tool_calls) {
39
+ totalChars += JSON.stringify(msg.tool_calls).length;
40
+ }
41
+ }
42
+ // Add overhead for system prompt, skills, etc.
43
+ totalChars += 2000;
44
+ // Rough estimation: ~4 characters per token
45
+ const tokens = Math.ceil(totalChars / 4);
46
+ const percentage = Math.min(100, Math.round((tokens / this.maxTokens) * 100));
47
+ return { tokens, percentage, maxTokens: this.maxTokens };
48
+ }
49
+ /**
50
+ * Format the context bar for display
51
+ */
52
+ formatBar(usage) {
53
+ const { percentage, tokens, maxTokens } = usage;
54
+ // Create progress bar (20 chars wide)
55
+ const filled = Math.round(percentage / 5);
56
+ const empty = 20 - filled;
57
+ let bar;
58
+ if (percentage < 60) {
59
+ bar = chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(empty));
60
+ }
61
+ else if (percentage < 80) {
62
+ bar = chalk_1.default.yellow('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(empty));
63
+ }
64
+ else {
65
+ bar = chalk_1.default.red('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(empty));
66
+ }
67
+ const tokensK = Math.round(tokens / 1000);
68
+ const maxTokensK = Math.round(maxTokens / 1000);
69
+ return `${bar} ${percentage}% | ${tokensK}k/${maxTokensK} tokens`;
70
+ }
71
+ /**
72
+ * Get the context bar string for current history
73
+ */
74
+ getContextBar(history) {
75
+ const usage = this.calculateUsage(history);
76
+ return this.formatBar(usage);
77
+ }
78
+ /**
79
+ * Check if context is at warning threshold (>=80%)
80
+ */
81
+ shouldCompact(history) {
82
+ const usage = this.calculateUsage(history);
83
+ return usage.percentage >= 80;
84
+ }
85
+ /**
86
+ * Set custom max tokens (for different models)
87
+ */
88
+ setMaxTokens(max) {
89
+ this.maxTokens = max;
90
+ }
91
+ }
92
+ exports.ContextVisualizer = ContextVisualizer;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * ConversationCompacter - Compact conversation to save tokens
4
+ * Summarizes conversation history while preserving important context
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.ConversationCompacter = void 0;
11
+ const inquirer_1 = __importDefault(require("inquirer"));
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ class ConversationCompacter {
14
+ /**
15
+ * Compact conversation history using AI
16
+ */
17
+ async compact(history, modelClient, options) {
18
+ const { keepSystemMessages = true, focusTopic } = options || {};
19
+ // Preserve system-style messages
20
+ const preserved = [];
21
+ const toCompact = [];
22
+ for (const msg of history) {
23
+ if (msg.role === 'system' || msg.role === 'tool') {
24
+ preserved.push(msg);
25
+ }
26
+ else {
27
+ toCompact.push(msg);
28
+ }
29
+ }
30
+ if (toCompact.length === 0) {
31
+ return history;
32
+ }
33
+ // Create compaction prompt
34
+ let compactPrompt = 'Please summarize the following conversation into a concise overview. ';
35
+ compactPrompt += 'Include:\n';
36
+ compactPrompt += '- The main topic/problem being discussed\n';
37
+ compactPrompt += '- Key decisions made\n';
38
+ compactPrompt += '- Important technical details\n';
39
+ compactPrompt += '- Current status/next steps\n\n';
40
+ if (focusTopic) {
41
+ compactPrompt += `Focus primarily on content related to: ${focusTopic}\n\n`;
42
+ }
43
+ compactPrompt += 'Return ONLY the summary, no other text.\n\n---\n\n';
44
+ for (const msg of toCompact.slice(-10)) { // Last 10 messages for context
45
+ compactPrompt += `${msg.role.toUpperCase()}: ${msg.content}\n\n`;
46
+ }
47
+ try {
48
+ // Call AI to summarize
49
+ const summaryResponse = await modelClient.chat([{ role: 'user', content: compactPrompt }], []);
50
+ const summary = summaryResponse.content;
51
+ // Create new compacted history
52
+ const newHistory = [...preserved];
53
+ // Add summary as a system message for context
54
+ newHistory.push({
55
+ role: 'system',
56
+ content: `[Previous Conversation Summary]\n${summary}`
57
+ });
58
+ return newHistory;
59
+ }
60
+ catch (error) {
61
+ console.error('Compaction failed:', error);
62
+ return history; // Return original if compaction fails
63
+ }
64
+ }
65
+ /**
66
+ * Prompt user to compact when threshold is reached
67
+ */
68
+ async promptIfCompactNeeded(percentage, history, modelClient) {
69
+ if (percentage < 80) {
70
+ return history;
71
+ }
72
+ console.log(chalk_1.default.yellow(`\n⚠️ Context is ${percentage}% full. Consider compacting to save tokens.`));
73
+ const { shouldCompact } = await inquirer_1.default.prompt([
74
+ {
75
+ type: 'confirm',
76
+ name: 'shouldCompact',
77
+ message: 'Compact conversation now?',
78
+ default: true
79
+ }
80
+ ]);
81
+ if (!shouldCompact) {
82
+ return history;
83
+ }
84
+ const { focusTopic } = await inquirer_1.default.prompt([
85
+ {
86
+ type: 'input',
87
+ name: 'focusTopic',
88
+ message: 'Focus on specific topic? (leave empty for general)',
89
+ default: ''
90
+ }
91
+ ]);
92
+ return await this.compact(history, modelClient, {
93
+ keepSystemMessages: true,
94
+ focusTopic: focusTopic || undefined
95
+ });
96
+ }
97
+ }
98
+ exports.ConversationCompacter = ConversationCompacter;