protoagent 0.0.2 → 0.0.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/index.js CHANGED
@@ -18,7 +18,9 @@ program
18
18
  .description('Interactive AI coding agent CLI with file system capabilities')
19
19
  .version('1.0.0')
20
20
  .option('--dangerously-accept-all', 'Auto-approve all shell commands and file operations without confirmation (DANGEROUS)')
21
- .option('--log-level <level>', 'Set logging level (ERROR, WARN, INFO, DEBUG, TRACE)', 'INFO');
21
+ .option('--log-level <level>', 'Set logging level (ERROR, WARN, INFO, DEBUG, TRACE)', 'INFO')
22
+ .option('--log-to-file', 'Enable file logging to ~/.protoagent/logs/')
23
+ .option('--log-dir <path>', 'Custom directory for log files (requires --log-to-file)');
22
24
  // Configuration management command
23
25
  program
24
26
  .command('config')
@@ -41,73 +43,99 @@ program
41
43
  await resetConfiguration();
42
44
  }
43
45
  else {
44
- console.log('Use --help to see available configuration options');
46
+ logger.consoleLog('Use --help to see available configuration options');
45
47
  }
46
48
  });
47
49
  async function respondToUser(userInput) {
50
+ logger.debug('šŸ¤– respondToUser called', { component: 'Main', inputLength: userInput.length });
48
51
  if (agenticLoop) {
52
+ logger.debug('āœ… Agentic loop available - processing input');
49
53
  await agenticLoop.processUserInput(userInput);
54
+ logger.debug('āœ… Agentic loop processing complete');
50
55
  }
51
56
  else {
57
+ logger.error('āŒ Agentic loop not initialized', { component: 'Main' });
52
58
  console.error('āŒ Agentic loop not initialized');
53
59
  }
54
60
  }
55
61
  async function initializeConfig() {
62
+ logger.debug('šŸ”§ Starting configuration initialization', { component: 'Config' });
56
63
  // Check if configuration exists
57
64
  if (await hasConfig()) {
65
+ logger.debug('šŸ“‹ Configuration file exists - loading', { component: 'Config' });
58
66
  try {
59
67
  config = await loadConfig();
68
+ logger.debug('āœ… Configuration loaded successfully', { component: 'Config', provider: config.provider, model: config.model });
60
69
  if (!validateConfig(config)) {
61
- console.log('āš ļø Configuration is invalid or corrupted.');
70
+ logger.warn('āš ļø Configuration validation failed', { component: 'Config' });
71
+ logger.consoleLog('āš ļø Configuration is invalid or corrupted.');
72
+ logger.info('āš ļø Invalid config message displayed to user');
62
73
  if (await promptReconfigure()) {
74
+ logger.debug('šŸ”„ User chose to reconfigure', { component: 'Config' });
63
75
  config = await setupConfig();
64
76
  }
65
77
  else {
66
- console.log('Cannot proceed without valid configuration.');
78
+ logger.error('āŒ User declined reconfiguration', { component: 'Config' });
79
+ logger.consoleLog('Cannot proceed without valid configuration.');
80
+ logger.error('āŒ Cannot proceed message displayed');
67
81
  process.exit(1);
68
82
  }
69
83
  }
70
84
  }
71
85
  catch (error) {
72
- console.log(`āŒ Error loading configuration: ${error.message}`);
86
+ logger.error('āŒ Error loading configuration', { component: 'Config', error: error.message });
87
+ logger.consoleLog(`āŒ Error loading configuration: ${error.message}`);
88
+ logger.info('āŒ Config error message displayed to user');
73
89
  if (await promptReconfigure()) {
90
+ logger.debug('šŸ”„ User chose to reconfigure after error', { component: 'Config' });
74
91
  config = await setupConfig();
75
92
  }
76
93
  else {
77
- console.log('Cannot proceed without valid configuration.');
94
+ logger.error('āŒ User declined reconfiguration after error', { component: 'Config' });
95
+ logger.consoleLog('Cannot proceed without valid configuration.');
96
+ logger.error('āŒ Cannot proceed after error message displayed');
78
97
  process.exit(1);
79
98
  }
80
99
  }
81
100
  }
82
101
  else {
102
+ logger.debug('šŸ“ No configuration exists - running setup', { component: 'Config' });
83
103
  // No configuration exists, run setup
84
104
  config = await setupConfig();
85
105
  }
106
+ logger.debug('šŸ”§ Setting tools configuration', { component: 'Config' });
86
107
  // Set config for tools
87
108
  setToolsConfig(config);
109
+ logger.debug('šŸ¤– Initializing OpenAI client', { component: 'Config', provider: config.provider });
88
110
  // Initialize OpenAI client with the configuration
89
111
  try {
90
112
  openaiClient = createOpenAIClient(config);
113
+ if (!openaiClient) {
114
+ throw new Error('Failed to create OpenAI client');
115
+ }
116
+ logger.debug('āœ… OpenAI client created successfully', { component: 'Config' });
117
+ logger.debug('šŸ”„ Creating agentic loop', { component: 'Config', maxIterations: 100, streamOutput: true });
91
118
  // Initialize the agentic loop
92
119
  agenticLoop = await createAgenticLoop(openaiClient, config, {
93
120
  maxIterations: 100,
94
121
  streamOutput: true
95
122
  });
96
- logger.debug(`āœ… Successfully initialized ProtoAgent`);
123
+ logger.debug(`āœ… Successfully initialized ProtoAgent`, { component: 'Config' });
97
124
  }
98
125
  catch (error) {
99
- logger.error(`āŒ Failed to initialize OpenAI client: ${error.message}`);
126
+ logger.error(`āŒ Failed to initialize OpenAI client: ${error.message}`, { component: 'Config', error: error.message });
100
127
  process.exit(1);
101
128
  }
102
129
  }
103
130
  async function startInteractiveMode() {
104
- console.log('šŸ¤– Welcome to ProtoAgent - Your AI Coding Assistant with File System & Shell Powers!');
105
- console.log('šŸ’” I can read, write, edit, search files and run shell commands in your project.');
106
- console.log('šŸ”§ Ask me to create files, analyze code, run npm commands, git operations, or execute any shell command.');
131
+ logger.debug('šŸš€ Starting interactive mode initialization');
132
+ logger.debug(' Parsing command line options');
107
133
  // Check for options
108
134
  const options = program.opts();
135
+ logger.debug('šŸ” Command line options parsed', { component: 'Main', options });
109
136
  // Set log level
110
137
  if (options.logLevel) {
138
+ logger.debug('šŸ”§ Setting log level', { component: 'Main', requestedLevel: options.logLevel });
111
139
  const logLevel = LogLevel[options.logLevel.toUpperCase()];
112
140
  if (logLevel !== undefined) {
113
141
  logger.setLevel(logLevel);
@@ -117,19 +145,50 @@ async function startInteractiveMode() {
117
145
  logger.warn(`āš ļø Invalid log level: ${options.logLevel}. Using INFO.`);
118
146
  }
119
147
  }
148
+ // Enable file logging if requested (BEFORE welcome messages)
149
+ if (options.logToFile) {
150
+ logger.debug('šŸ“ Enabling file logging', { component: 'Main', logDir: options.logDir || 'default' });
151
+ logger.enableFileLogging(options.logDir);
152
+ logger.info('šŸ“ File logging enabled');
153
+ }
120
154
  // Check for dangerous flag
121
155
  if (options.dangerouslyAcceptAll) {
156
+ logger.debug('āš ļø Enabling dangerous mode', { component: 'Main' });
122
157
  logger.warn('āš ļø DANGER MODE: All shell commands and file operations will be auto-approved without confirmation!');
123
158
  setToolsDangerouslyAcceptAll(true);
124
159
  }
125
- console.log('🚪 Type "exit" to quit.\n');
160
+ // NOW display welcome messages (after file logging is enabled)
161
+ logger.consoleLog('šŸ¤– Welcome to ProtoAgent - Your AI Coding Assistant with File System & Shell Powers!');
162
+ logger.debug('šŸ¤– Welcome message displayed');
163
+ logger.consoleLog('šŸ’” I can read, write, edit, search files and run shell commands in your project.');
164
+ logger.consoleLog('šŸ”§ Ask me to create files, analyze code, run npm commands, git operations, or execute any shell command.');
165
+ logger.debug('šŸ’” Capability messages displayed');
166
+ logger.consoleLog('🚪 Type "exit" to quit.');
167
+ logger.debug('🚪 Exit instruction displayed');
168
+ logger.debug('šŸ”§ Initializing configuration');
126
169
  // Initialize configuration
127
170
  await initializeConfig();
128
- logger.info(`🧠 Using ${config.provider} with model: ${config.model}`);
171
+ // Set up cleanup handlers for file logging
172
+ logger.debug('šŸ›”ļø Setting up cleanup handlers');
173
+ process.on('SIGINT', () => {
174
+ logger.debug('šŸ›‘ SIGINT received - cleaning up');
175
+ logger.consoleLog('\nšŸ‘‹ Goodbye! Happy coding!');
176
+ logger.info('šŸ‘‹ Goodbye message displayed (SIGINT)');
177
+ logger.disableFileLogging();
178
+ process.exit(0);
179
+ });
180
+ process.on('SIGTERM', () => {
181
+ logger.debug('šŸ›‘ SIGTERM received - cleaning up');
182
+ logger.disableFileLogging();
183
+ process.exit(0);
184
+ });
185
+ logger.consoleLog(`🧠 Using ${config.provider} with model: ${config.model}`);
129
186
  // Show current working directory
130
- logger.info(`šŸ“ Working directory: ${process.cwd()}`);
131
- console.log(`šŸ“ Working directory: ${process.cwd()}\n`);
187
+ logger.consoleLog(`šŸ“ Working directory: ${process.cwd()}`);
188
+ logger.debug('šŸ“ Working directory displayed to user');
189
+ logger.debug('šŸ”„ Starting main interaction loop');
132
190
  while (true) {
191
+ logger.trace('šŸ“„ Waiting for user input');
133
192
  const { input } = await inquirer.prompt([
134
193
  {
135
194
  type: 'input',
@@ -137,15 +196,23 @@ async function startInteractiveMode() {
137
196
  message: 'protoagent>',
138
197
  }
139
198
  ]);
199
+ logger.debug('šŸ“ User input received', { component: 'Main', inputLength: input.length, isEmpty: input.trim() === '' });
140
200
  if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
141
- console.log('šŸ‘‹ Goodbye! Happy coding!');
201
+ logger.debug('🚪 Exit command received');
202
+ logger.consoleLog('šŸ‘‹ Goodbye! Happy coding!');
203
+ logger.info('šŸ‘‹ Goodbye message displayed (normal exit)');
204
+ // Clean up file logging
205
+ logger.debug('🧹 Cleaning up file logging');
206
+ logger.disableFileLogging();
142
207
  break;
143
208
  }
144
209
  if (input.trim() === '') {
210
+ logger.trace('ā­ļø Empty input - continuing');
145
211
  continue;
146
212
  }
213
+ logger.debug('šŸ¤– Forwarding input to agent', { component: 'Main', input: input.slice(0, 100) + (input.length > 100 ? '...' : '') });
147
214
  await respondToUser(input);
148
- console.log(); // Add extra newline for spacing
215
+ logger.trace('āœ… User input processing complete');
149
216
  }
150
217
  }
151
218
  program
@@ -29,6 +29,7 @@ import { viewDirectoryTree } from './view-directory-tree.js';
29
29
  import { searchFiles } from './search-files.js';
30
30
  import { runShellCommand } from './run-shell-command.js';
31
31
  import { handleTodoTool } from './todo.js';
32
+ import { logger } from '../utils/logger.js';
32
33
  // Consolidated tool definitions
33
34
  export const tools = [
34
35
  readFileTool,
@@ -44,41 +45,96 @@ export const tools = [
44
45
  ];
45
46
  // Tool handler function
46
47
  export async function handleToolCall(toolName, args) {
48
+ logger.debug('šŸ› ļø handleToolCall invoked', {
49
+ component: 'ToolHandler',
50
+ toolName,
51
+ argsKeys: Object.keys(args || {})
52
+ });
47
53
  try {
54
+ const startTime = Date.now();
55
+ let result;
48
56
  switch (toolName) {
49
57
  case 'read_file':
58
+ logger.debug('šŸ“– Executing read_file', { component: 'ToolHandler', filePath: args.file_path });
50
59
  const content = await readFile(args.file_path, args.offset, args.limit);
51
- return `File content of ${args.file_path}:\n\n${content}`;
60
+ result = `File content of ${args.file_path}:\n\n${content}`;
61
+ break;
52
62
  case 'write_file':
53
- const writeResult = await writeFile(args.file_path, args.content);
54
- return writeResult;
63
+ logger.debug('āœļø Executing write_file', { component: 'ToolHandler', filePath: args.file_path, contentLength: args.content?.length });
64
+ result = await writeFile(args.file_path, args.content);
65
+ break;
55
66
  case 'edit_file':
56
- const editResult = await editFile(args.file_path, args.old_string, args.new_string, args.expected_replacements);
57
- return editResult;
67
+ logger.debug('āœ‚ļø Executing edit_file', {
68
+ component: 'ToolHandler',
69
+ filePath: args.file_path,
70
+ oldStringLength: args.old_string?.length,
71
+ newStringLength: args.new_string?.length,
72
+ expectedReplacements: args.expected_replacements
73
+ });
74
+ result = await editFile(args.file_path, args.old_string, args.new_string, args.expected_replacements);
75
+ break;
58
76
  case 'list_directory':
77
+ logger.debug('šŸ“ Executing list_directory', { component: 'ToolHandler', directoryPath: args.directory_path });
59
78
  const listResult = await listDirectory(args.directory_path);
60
- return `Contents of ${args.directory_path}:\n\n${listResult}`;
79
+ result = `Contents of ${args.directory_path}:\n\n${listResult}`;
80
+ break;
61
81
  case 'create_directory':
62
- const createResult = await createDirectory(args.directory_path);
63
- return createResult;
82
+ logger.debug('šŸ“‚ Executing create_directory', { component: 'ToolHandler', directoryPath: args.directory_path });
83
+ result = await createDirectory(args.directory_path);
84
+ break;
64
85
  case 'view_directory_tree':
65
- const treeResult = await viewDirectoryTree(args.directory_path, args.max_depth);
66
- return treeResult;
86
+ logger.debug('🌳 Executing view_directory_tree', {
87
+ component: 'ToolHandler',
88
+ directoryPath: args.directory_path,
89
+ maxDepth: args.max_depth
90
+ });
91
+ result = await viewDirectoryTree(args.directory_path, args.max_depth);
92
+ break;
67
93
  case 'search_files':
68
- const searchResult = await searchFiles(args.search_term, args.directory_path, args.case_sensitive, args.file_extensions);
69
- return searchResult;
94
+ logger.debug('šŸ” Executing search_files', {
95
+ component: 'ToolHandler',
96
+ searchTerm: args.search_term,
97
+ directoryPath: args.directory_path,
98
+ caseSensitive: args.case_sensitive,
99
+ fileExtensions: args.file_extensions
100
+ });
101
+ result = await searchFiles(args.search_term, args.directory_path, args.case_sensitive, args.file_extensions);
102
+ break;
70
103
  case 'run_shell_command':
71
- const shellResult = await runShellCommand(args.command, args.args || [], args.timeout_ms || 30000);
72
- return shellResult;
104
+ logger.debug('🐚 Executing run_shell_command', {
105
+ component: 'ToolHandler',
106
+ command: args.command,
107
+ argsCount: args.args?.length || 0,
108
+ timeout: args.timeout_ms || 30000
109
+ });
110
+ result = await runShellCommand(args.command, args.args || [], args.timeout_ms || 30000);
111
+ break;
73
112
  case 'todo_read':
74
113
  case 'todo_write':
75
- return await handleTodoTool(toolName, args);
114
+ logger.debug('šŸ“‹ Executing todo tool', { component: 'ToolHandler', toolName });
115
+ result = await handleTodoTool(toolName, args);
116
+ break;
76
117
  default:
77
- return `Error: Unknown tool ${toolName}`;
118
+ logger.error('āŒ Unknown tool requested', { component: 'ToolHandler', toolName });
119
+ result = `Error: Unknown tool ${toolName}`;
78
120
  }
121
+ const executionTime = Date.now() - startTime;
122
+ logger.debug('āœ… Tool execution completed', {
123
+ component: 'ToolHandler',
124
+ toolName,
125
+ executionTime,
126
+ resultLength: result.length,
127
+ success: !result.startsWith('Error:')
128
+ });
129
+ return result;
79
130
  }
80
131
  catch (error) {
81
132
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
133
+ logger.error('āŒ Tool execution failed with exception', {
134
+ component: 'ToolHandler',
135
+ toolName,
136
+ error: errorMessage
137
+ });
82
138
  return `Error executing ${toolName}: ${errorMessage}`;
83
139
  }
84
140
  }
@@ -1,5 +1,6 @@
1
1
  import { spawn } from 'child_process';
2
2
  import inquirer from 'inquirer';
3
+ import { logger } from '../utils/logger.js';
3
4
  // Current working directory for file operations
4
5
  const workingDirectory = process.cwd();
5
6
  // Global flags and session state
@@ -39,10 +40,10 @@ async function runShellCommandWithRetry(command, args = [], timeoutMs = 30000, r
39
40
  // Check if we should auto-approve (only show approval prompt on first attempt)
40
41
  if (retryCount === 0) {
41
42
  if (dangerouslyAcceptAll) {
42
- console.log(`šŸš€ Auto-executing (--dangerously-accept-all): ${commandString}`);
43
+ logger.consoleLog(`šŸš€ Auto-executing (--dangerously-accept-all): ${commandString}`);
43
44
  }
44
45
  else if (approvedCommandsForSession.has(baseCommand)) {
45
- console.log(`šŸš€ Auto-executing (${baseCommand} approved for session): ${commandString}`);
46
+ logger.consoleLog(`šŸš€ Auto-executing (${baseCommand} approved for session): ${commandString}`);
46
47
  }
47
48
  else {
48
49
  // Check if it's a safe command
@@ -54,12 +55,12 @@ async function runShellCommandWithRetry(command, args = [], timeoutMs = 30000, r
54
55
  (!normalizedSafe.includes(' ') && normalizedFull.split(' ')[0] === normalizedSafe);
55
56
  });
56
57
  if (isSafeCommand) {
57
- console.log(`🟢 Executing safe command: ${commandString}`);
58
+ logger.consoleLog(`🟢 Executing safe command: ${commandString}`);
58
59
  }
59
60
  else {
60
61
  // Require user confirmation with enhanced options
61
- console.log(`\nšŸ” Shell Command Requested: ${commandString}`);
62
- console.log(`šŸ“ Working Directory: ${workingDirectory}`);
62
+ logger.consoleLog(`\nšŸ” Shell Command Requested: ${commandString}`);
63
+ logger.consoleLog(`šŸ“ Working Directory: ${workingDirectory}`);
63
64
  const { choice } = await inquirer.prompt([
64
65
  {
65
66
  type: 'list',
@@ -75,12 +76,12 @@ async function runShellCommandWithRetry(command, args = [], timeoutMs = 30000, r
75
76
  ]);
76
77
  switch (choice) {
77
78
  case 'execute':
78
- console.log(`šŸš€ Executing: ${commandString}`);
79
+ logger.consoleLog(`šŸš€ Executing: ${commandString}`);
79
80
  break;
80
81
  case 'approve_session':
81
82
  approvedCommandsForSession.add(baseCommand);
82
- console.log(`šŸ”“ "${baseCommand}" approved for session - all future ${baseCommand} commands will auto-execute`);
83
- console.log(`šŸš€ Executing: ${commandString}`);
83
+ logger.consoleLog(`šŸ”“ "${baseCommand}" approved for session - all future ${baseCommand} commands will auto-execute`);
84
+ logger.consoleLog(`šŸš€ Executing: ${commandString}`);
84
85
  break;
85
86
  case 'cancel':
86
87
  return getSuggestion(commandString);
@@ -91,7 +92,7 @@ async function runShellCommandWithRetry(command, args = [], timeoutMs = 30000, r
91
92
  }
92
93
  }
93
94
  else {
94
- console.log(`šŸ”„ Retry attempt ${retryCount}/${maxRetries}: ${commandString}`);
95
+ logger.consoleLog(`šŸ”„ Retry attempt ${retryCount}/${maxRetries}: ${commandString}`);
95
96
  }
96
97
  return new Promise((resolve, reject) => {
97
98
  const child = spawn(command, args, {
@@ -154,18 +155,18 @@ async function runShellCommandWithRetry(command, args = [], timeoutMs = 30000, r
154
155
  }
155
156
  async function analyzeTimeoutAndRetry(command, args = [], originalTimeout, retryCount) {
156
157
  const commandString = `${command} ${args.join(' ')}`;
157
- console.log(`ā±ļø Command timed out after ${originalTimeout}ms: ${commandString}`);
158
- console.log(`šŸ” Analyzing timeout cause and preparing retry...`);
158
+ logger.consoleLog(`ā±ļø Command timed out after ${originalTimeout}ms: ${commandString}`);
159
+ logger.consoleLog(`šŸ” Analyzing timeout cause and preparing retry...`);
159
160
  // Analyze the command to understand why it might have timed out
160
161
  const analysis = analyzeCommandForTimeout(command, args);
161
- console.log(`šŸ’” Timeout analysis: ${analysis.reason}`);
162
+ logger.consoleLog(`šŸ’” Timeout analysis: ${analysis.reason}`);
162
163
  if (analysis.suggestedArgs.length > 0) {
163
- console.log(`šŸ”§ Suggested fix: ${command} ${analysis.suggestedArgs.join(' ')}`);
164
+ logger.consoleLog(`šŸ”§ Suggested fix: ${command} ${analysis.suggestedArgs.join(' ')}`);
164
165
  // Try with suggested arguments
165
166
  return await runShellCommandWithRetry(command, analysis.suggestedArgs, analysis.suggestedTimeout, retryCount + 1);
166
167
  }
167
168
  else if (analysis.suggestedTimeout > originalTimeout) {
168
- console.log(`ā° Increasing timeout to ${analysis.suggestedTimeout}ms for long-running operation`);
169
+ logger.consoleLog(`ā° Increasing timeout to ${analysis.suggestedTimeout}ms for long-running operation`);
169
170
  // Try with longer timeout
170
171
  return await runShellCommandWithRetry(command, args, analysis.suggestedTimeout, retryCount + 1);
171
172
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Conversation history compaction utilities
3
3
  */
4
+ import { logger } from './logger.js';
4
5
  /**
5
6
  * Provides the system prompt for the history compression process.
6
7
  * This prompt instructs the model to act as a specialized state manager,
@@ -69,14 +70,14 @@ The structure MUST be as follows:
69
70
  * Compact conversation history by creating a summary
70
71
  */
71
72
  export async function compactConversation(client, model, messages, recentMessagesToKeep = 5) {
72
- console.log('\nšŸ—œļø Compacting conversation history to manage context window...');
73
+ logger.consoleLog('\nšŸ—œļø Compacting conversation history to manage context window...');
73
74
  // Keep the system message and recent messages
74
75
  const systemMessage = messages.find(m => m.role === 'system');
75
76
  const recentMessages = messages.slice(-recentMessagesToKeep);
76
77
  // Get the history to compress (excluding system and recent messages)
77
78
  const historyToCompress = messages.slice(systemMessage ? 1 : 0, -recentMessagesToKeep);
78
79
  if (historyToCompress.length === 0) {
79
- console.log(' No history to compress, keeping current messages');
80
+ logger.consoleLog(' No history to compress, keeping current messages');
80
81
  return messages;
81
82
  }
82
83
  try {
@@ -100,7 +101,7 @@ export async function compactConversation(client, model, messages, recentMessage
100
101
  });
101
102
  const compressionSummary = response.choices[0]?.message?.content;
102
103
  if (!compressionSummary) {
103
- console.log(' Failed to generate compression summary, keeping original messages');
104
+ logger.consoleLog(' Failed to generate compression summary, keeping original messages');
104
105
  return messages;
105
106
  }
106
107
  // Create the compressed history message
@@ -118,12 +119,12 @@ export async function compactConversation(client, model, messages, recentMessage
118
119
  compactedMessages.push(compressedMessage);
119
120
  // Add recent messages
120
121
  compactedMessages.push(...recentMessages);
121
- console.log(` āœ… Compressed ${historyToCompress.length} messages into summary`);
122
- console.log(` šŸ“ Keeping ${recentMessages.length} recent messages`);
122
+ logger.consoleLog(` āœ… Compressed ${historyToCompress.length} messages into summary`);
123
+ logger.consoleLog(` šŸ“ Keeping ${recentMessages.length} recent messages`);
123
124
  return compactedMessages;
124
125
  }
125
126
  catch (error) {
126
- console.log(` āŒ Compression failed: ${error}. Keeping original messages.`);
127
+ logger.consoleLog(` āŒ Compression failed: ${error}. Keeping original messages.`);
127
128
  return messages;
128
129
  }
129
130
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Cost tracking and token counting utilities
3
3
  */
4
+ import { logger } from './logger.js';
4
5
  /**
5
6
  * Rough token estimation for OpenAI models
6
7
  * This is approximate - actual tokenization may vary
@@ -72,11 +73,11 @@ export function getContextInfo(messages, modelConfig) {
72
73
  * Log usage and cost information
73
74
  */
74
75
  export function logUsageInfo(usage, contextInfo, modelConfig) {
75
- console.log(`šŸ’° Usage: ${usage.inputTokens} in + ${usage.outputTokens} out = ${usage.totalTokens} tokens`);
76
- console.log(`šŸ’ø Estimated cost: $${usage.estimatedCost.toFixed(6)}`);
77
- console.log(`šŸ“Š Context: ${contextInfo.currentTokens}/${contextInfo.maxTokens} tokens (${contextInfo.utilizationPercentage.toFixed(1)}%)`);
76
+ logger.consoleLog(`šŸ’° Usage: ${usage.inputTokens} in + ${usage.outputTokens} out = ${usage.totalTokens} tokens`);
77
+ logger.consoleLog(`šŸ’ø Estimated cost: $${usage.estimatedCost.toFixed(6)}`);
78
+ logger.consoleLog(`šŸ“Š Context: ${contextInfo.currentTokens}/${contextInfo.maxTokens} tokens (${contextInfo.utilizationPercentage.toFixed(1)}%)`);
78
79
  if (contextInfo.needsCompaction) {
79
- console.log(`āš ļø Context approaching limit - automatic compaction will trigger soon`);
80
+ logger.consoleLog(`āš ļø Context approaching limit - automatic compaction will trigger soon`);
80
81
  }
81
82
  }
82
83
  /**
@@ -33,21 +33,9 @@ export async function requestFileOperationApproval(context) {
33
33
  return true;
34
34
  }
35
35
  // Show detailed information about the operation
36
- console.log(`\nšŸ” File Operation Requested: ${operation.toUpperCase()}`);
37
- console.log(`šŸ“ File: ${filePath}`);
38
- console.log(`šŸ“ Description: ${description}`);
39
- if (contentPreview) {
40
- console.log(`\nšŸ“„ Content Preview:`);
41
- const lines = contentPreview.split('\n');
42
- if (lines.length > 10) {
43
- console.log(lines.slice(0, 5).join('\n'));
44
- console.log(`... (${lines.length - 10} more lines) ...`);
45
- console.log(lines.slice(-5).join('\n'));
46
- }
47
- else {
48
- console.log(contentPreview);
49
- }
50
- }
36
+ logger.consoleLog(`\nšŸ” File Operation Requested: ${operation.toUpperCase()}`);
37
+ logger.consoleLog(`šŸ“ File: ${filePath}`);
38
+ logger.consoleLog(`šŸ“ Description: ${description}`);
51
39
  // Ask for user approval with enhanced options
52
40
  const { choice } = await inquirer.prompt([
53
41
  {
@@ -89,43 +77,43 @@ export async function requestFileOperationApproval(context) {
89
77
  */
90
78
  async function showFileOperationDetails(context) {
91
79
  const { operation, filePath, description, contentPreview } = context;
92
- console.log(`\nšŸ“‹ Detailed File Operation Information:`);
93
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
94
- console.log(`šŸ”§ Operation Type: ${operation.toUpperCase()}`);
95
- console.log(`šŸ“ Target File: ${filePath}`);
96
- console.log(`šŸ“ Description: ${description}`);
97
- console.log(`šŸ“‚ Working Directory: ${process.cwd()}`);
80
+ logger.consoleLog(`\nšŸ“‹ Detailed File Operation Information:`);
81
+ logger.consoleLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
82
+ logger.consoleLog(`šŸ”§ Operation Type: ${operation.toUpperCase()}`);
83
+ logger.consoleLog(`šŸ“ Target File: ${filePath}`);
84
+ logger.consoleLog(`šŸ“ Description: ${description}`);
85
+ logger.consoleLog(`šŸ“‚ Working Directory: ${process.cwd()}`);
98
86
  // Show file status
99
87
  try {
100
88
  const fs = await import('fs/promises');
101
89
  const stats = await fs.stat(filePath);
102
- console.log(`šŸ“Š File exists: YES`);
103
- console.log(`šŸ“… Last modified: ${stats.mtime.toLocaleString()}`);
104
- console.log(`šŸ“ File size: ${stats.size} bytes`);
90
+ logger.consoleLog(`šŸ“Š File exists: YES`);
91
+ logger.consoleLog(`šŸ“… Last modified: ${stats.mtime.toLocaleString()}`);
92
+ logger.consoleLog(`šŸ“ File size: ${stats.size} bytes`);
105
93
  }
106
94
  catch (error) {
107
- console.log(`šŸ“Š File exists: NO (will be created)`);
95
+ logger.consoleLog(`šŸ“Š File exists: NO (will be created)`);
108
96
  }
109
97
  // Show full content if available
110
98
  if (contentPreview) {
111
- console.log(`\nšŸ“„ Full Content Preview:`);
112
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
113
- console.log(contentPreview);
114
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
99
+ logger.consoleLog(`\nšŸ“„ Full Content Preview:`);
100
+ logger.consoleLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
101
+ logger.consoleLog(contentPreview);
102
+ logger.consoleLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
115
103
  }
116
104
  // Show security information
117
- console.log(`\nšŸ”’ Security Information:`);
118
- console.log(`• File path is validated and restricted to working directory`);
119
- console.log(`• Operation will be performed atomically with backup`);
120
- console.log(`• You can undo changes using git if needed`);
121
- console.log(`\nšŸ’” Recommendation:`);
105
+ logger.consoleLog(`\nšŸ”’ Security Information:`);
106
+ logger.consoleLog(`• File path is validated and restricted to working directory`);
107
+ logger.consoleLog(`• Operation will be performed atomically with backup`);
108
+ logger.consoleLog(`• You can undo changes using git if needed`);
109
+ logger.consoleLog(`\nšŸ’” Recommendation:`);
122
110
  if (operation === 'write' && contentPreview) {
123
- console.log(`• WRITE operation will ${contentPreview.includes('export') ? 'create a new file' : 'replace file content'}`);
124
- console.log(`• Consider approving if the content looks correct`);
111
+ logger.consoleLog(`• WRITE operation will ${contentPreview.includes('export') ? 'create a new file' : 'replace file content'}`);
112
+ logger.consoleLog(`• Consider approving if the content looks correct`);
125
113
  }
126
114
  else if (operation === 'edit') {
127
- console.log(`• EDIT operation will make targeted changes to existing file`);
128
- console.log(`• Generally safer than write operations`);
115
+ logger.consoleLog(`• EDIT operation will make targeted changes to existing file`);
116
+ logger.consoleLog(`• Generally safer than write operations`);
129
117
  }
130
118
  await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
131
119
  }
@@ -151,13 +139,13 @@ export function clearSessionApprovals() {
151
139
  */
152
140
  export function showApprovalStatus() {
153
141
  const status = getSessionApprovalStatus();
154
- console.log('\nšŸ“Š Current File Operation Approval Status:');
155
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
156
- console.log(`šŸš€ Dangerous Mode (auto-approve all): ${status.dangerousMode ? 'āœ… ENABLED' : 'āŒ Disabled'}`);
157
- console.log(`āœļø Edit operations for session: ${status.editApproved ? 'āœ… Auto-approved' : 'āŒ Require approval'}`);
158
- console.log(`šŸ“ Write operations for session: ${status.writeApproved ? 'āœ… Auto-approved' : 'āŒ Require approval'}`);
142
+ logger.consoleLog('\nšŸ“Š Current File Operation Approval Status:');
143
+ logger.consoleLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
144
+ logger.consoleLog(`šŸš€ Dangerous Mode (auto-approve all): ${status.dangerousMode ? 'āœ… ENABLED' : 'āŒ Disabled'}`);
145
+ logger.consoleLog(`āœļø Edit operations for session: ${status.editApproved ? 'āœ… Auto-approved' : 'āŒ Require approval'}`);
146
+ logger.consoleLog(`šŸ“ Write operations for session: ${status.writeApproved ? 'āœ… Auto-approved' : 'āŒ Require approval'}`);
159
147
  if (status.dangerousMode) {
160
- console.log(`\nāš ļø WARNING: Dangerous mode is enabled - all file operations will be auto-approved!`);
148
+ logger.consoleLog(`\nāš ļø WARNING: Dangerous mode is enabled - all file operations will be auto-approved!`);
161
149
  }
162
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
150
+ logger.consoleLog(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
163
151
  }