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.
@@ -4,28 +4,47 @@
4
4
  import OpenAI from 'openai';
5
5
  import { geminiProvider, cerebrasProvider, getModelConfig } from './providers.js';
6
6
  import { estimateTokens, getContextInfo } from '../utils/cost-tracker.js';
7
+ import { logger } from '../utils/logger.js';
7
8
  /**
8
9
  * Create OpenAI client from configuration
9
10
  */
10
11
  export function createOpenAIClient(config) {
12
+ logger.debug('šŸ¤– Creating OpenAI client', {
13
+ component: 'OpenAIClient',
14
+ provider: config.provider,
15
+ model: config.model
16
+ });
11
17
  if (config.provider === 'openai') {
18
+ logger.debug('šŸ”‘ Using OpenAI provider', { component: 'OpenAIClient' });
12
19
  return new OpenAI({
13
20
  apiKey: config.credentials.OPENAI_API_KEY
14
21
  });
15
22
  }
16
23
  if (config.provider === 'gemini') {
24
+ logger.debug('šŸ”‘ Using Gemini provider', {
25
+ component: 'OpenAIClient',
26
+ baseURL: geminiProvider.baseURL
27
+ });
17
28
  return new OpenAI({
18
29
  apiKey: config.credentials.GEMINI_API_KEY,
19
30
  baseURL: geminiProvider.baseURL
20
31
  });
21
32
  }
22
33
  if (config.provider === 'cerebras') {
34
+ logger.debug('šŸ”‘ Using Cerebras provider', {
35
+ component: 'OpenAIClient',
36
+ baseURL: cerebrasProvider.baseURL
37
+ });
23
38
  return new OpenAI({
24
39
  apiKey: config.credentials.CEREBRAS_API_KEY,
25
40
  baseURL: cerebrasProvider.baseURL
26
41
  });
27
42
  }
28
- throw new Error(`Unsupported provider: ${config.provider}`);
43
+ logger.error('āŒ Unknown provider', {
44
+ component: 'OpenAIClient',
45
+ provider: config.provider
46
+ });
47
+ throw new Error(`Unknown provider: ${config.provider}`);
29
48
  }
30
49
  /**
31
50
  * Sleep for a given number of milliseconds
@@ -37,12 +56,22 @@ function sleep(ms) {
37
56
  * Check if an error is retryable
38
57
  */
39
58
  function isRetryableError(error) {
59
+ logger.debug('šŸ” Checking if error is retryable', {
60
+ component: 'OpenAIClient',
61
+ status: error?.status,
62
+ code: error?.code
63
+ });
40
64
  // Check for rate limiting (429)
41
65
  if (error?.status === 429) {
66
+ logger.debug('ā³ Error is rate limit (429)', { component: 'OpenAIClient' });
42
67
  return true;
43
68
  }
44
69
  // Check for server errors (5xx)
45
70
  if (error?.status >= 500 && error?.status < 600) {
71
+ logger.debug('šŸ”§ Error is server error (5xx)', {
72
+ component: 'OpenAIClient',
73
+ status: error.status
74
+ });
46
75
  return true;
47
76
  }
48
77
  // Check for network/connection errors
@@ -51,25 +80,50 @@ function isRetryableError(error) {
51
80
  error?.code === 'ECONNREFUSED' ||
52
81
  error?.message?.includes('network') ||
53
82
  error?.message?.includes('timeout')) {
83
+ logger.debug('🌐 Error is network related', {
84
+ component: 'OpenAIClient',
85
+ code: error.code,
86
+ message: error.message
87
+ });
54
88
  return true;
55
89
  }
90
+ logger.debug('āŒ Error is not retryable', { component: 'OpenAIClient' });
56
91
  return false;
57
92
  }
58
93
  /**
59
94
  * Get appropriate delay for rate limiting based on error
60
95
  */
61
96
  function getRateLimitDelay(error, attempt) {
97
+ logger.debug('ā±ļø Calculating rate limit delay', {
98
+ component: 'OpenAIClient',
99
+ attempt,
100
+ retryAfterHeader: error?.headers?.['retry-after']
101
+ });
62
102
  // Check for Retry-After header (OpenAI provides this for rate limits)
63
103
  if (error?.headers?.['retry-after']) {
64
104
  const retryAfter = parseInt(error.headers['retry-after'], 10);
65
105
  if (!isNaN(retryAfter)) {
66
- return retryAfter * 1000; // Convert to milliseconds
106
+ const delayMs = retryAfter * 1000;
107
+ logger.debug('šŸ“Ø Using Retry-After header delay', {
108
+ component: 'OpenAIClient',
109
+ retryAfter,
110
+ delayMs
111
+ });
112
+ return delayMs; // Convert to milliseconds
67
113
  }
68
114
  }
69
115
  // Default exponential backoff for rate limits: 2^attempt seconds (min 2s, max 60s)
70
116
  const baseDelay = Math.min(Math.pow(2, attempt) * 1000, 60000);
71
117
  const jitter = Math.random() * 1000; // Add jitter to prevent thundering herd
72
- return Math.max(baseDelay + jitter, 2000); // Minimum 2 seconds
118
+ const finalDelay = Math.max(baseDelay + jitter, 2000); // Minimum 2 seconds
119
+ logger.debug('⚔ Using exponential backoff delay', {
120
+ component: 'OpenAIClient',
121
+ attempt,
122
+ baseDelay,
123
+ jitter,
124
+ finalDelay
125
+ });
126
+ return finalDelay;
73
127
  }
74
128
  /**
75
129
  * Get delay for general retryable errors
@@ -78,7 +132,15 @@ function getRetryDelay(attempt) {
78
132
  // Exponential backoff: 1, 2, 4 seconds with jitter
79
133
  const baseDelay = Math.pow(2, attempt - 1) * 1000;
80
134
  const jitter = Math.random() * 500;
81
- return Math.min(baseDelay + jitter, 4000); // Max 4 seconds
135
+ const finalDelay = Math.min(baseDelay + jitter, 4000); // Max 4 seconds
136
+ logger.debug('šŸ”„ Calculating retry delay', {
137
+ component: 'OpenAIClient',
138
+ attempt,
139
+ baseDelay,
140
+ jitter,
141
+ finalDelay
142
+ });
143
+ return finalDelay;
82
144
  }
83
145
  /**
84
146
  * Create chat completion with OpenAI with retry logic and cost tracking
@@ -86,6 +148,13 @@ function getRetryDelay(attempt) {
86
148
  export async function createChatCompletion(client, params, config, messages) {
87
149
  const maxRetries = 3;
88
150
  let lastError;
151
+ logger.debug('šŸš€ Starting chat completion request', {
152
+ component: 'OpenAIClient',
153
+ provider: config.provider,
154
+ model: config.model,
155
+ messageCount: messages.length,
156
+ maxRetries
157
+ });
89
158
  // Get model configuration for cost tracking
90
159
  const modelConfig = getModelConfig(config.provider, config.model);
91
160
  // Estimate input tokens for cost calculation
@@ -95,37 +164,78 @@ export async function createChatCompletion(client, params, config, messages) {
95
164
  }
96
165
  return total;
97
166
  }, 0);
167
+ logger.debug('šŸ“Š Token estimation complete', {
168
+ component: 'OpenAIClient',
169
+ estimatedInputTokens,
170
+ hasModelConfig: !!modelConfig
171
+ });
98
172
  // Log context information before making the request
99
173
  if (modelConfig) {
100
174
  const contextInfo = getContextInfo(messages, modelConfig);
101
- console.log(`šŸ“Š Context: ${contextInfo.currentTokens}/${contextInfo.maxTokens} tokens (${contextInfo.utilizationPercentage.toFixed(1)}%)`);
175
+ logger.consoleLog(`šŸ“Š Context: ${contextInfo.currentTokens}/${contextInfo.maxTokens} tokens (${contextInfo.utilizationPercentage.toFixed(1)}%)`, {
176
+ component: 'OpenAIClient',
177
+ ...contextInfo
178
+ });
102
179
  if (contextInfo.needsCompaction) {
103
- console.log(`āš ļø Context approaching limit - automatic compaction will trigger soon`);
180
+ logger.consoleLog(`āš ļø Context approaching limit - automatic compaction will trigger soon`, {
181
+ component: 'OpenAIClient',
182
+ needsCompaction: true
183
+ });
104
184
  }
105
185
  }
106
186
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
187
+ logger.debug(`šŸŽÆ Attempt ${attempt}/${maxRetries}`, {
188
+ component: 'OpenAIClient',
189
+ attempt,
190
+ maxRetries
191
+ });
107
192
  try {
193
+ logger.debug('šŸ“” Making API request', {
194
+ component: 'OpenAIClient',
195
+ stream: params.stream || true,
196
+ includeUsage: true
197
+ });
108
198
  const stream = await client.chat.completions.create({
109
199
  ...params,
110
200
  stream: true,
111
201
  stream_options: { include_usage: true }
112
202
  });
203
+ logger.debug('āœ… API request successful', {
204
+ component: 'OpenAIClient',
205
+ attempt,
206
+ estimatedInputTokens
207
+ });
113
208
  return { stream, estimatedInputTokens };
114
209
  }
115
210
  catch (error) {
116
211
  lastError = error;
212
+ logger.debug('āŒ API request failed', {
213
+ component: 'OpenAIClient',
214
+ attempt,
215
+ errorStatus: error?.status,
216
+ errorCode: error?.code,
217
+ errorMessage: error?.message
218
+ });
117
219
  // Handle rate limiting (429) specially
118
220
  if (error?.status === 429) {
119
221
  const delay = getRateLimitDelay(error, attempt);
120
222
  const seconds = Math.round(delay / 1000);
121
223
  if (attempt < maxRetries) {
122
- console.log(`\nā³ Rate limited by API. Waiting ${seconds} seconds before retry (attempt ${attempt}/${maxRetries})...`);
224
+ logger.consoleLog(`\nā³ Rate limited by API. Waiting ${seconds} seconds before retry (attempt ${attempt}/${maxRetries})...`, {
225
+ component: 'OpenAIClient',
226
+ attempt,
227
+ maxRetries,
228
+ delayMs: delay
229
+ });
123
230
  await sleep(delay);
124
231
  continue;
125
232
  }
126
233
  else {
127
- console.error('\nāŒ Rate limit exceeded. Maximum retries reached.');
128
- console.log('šŸ’” Tip: Consider upgrading your API plan or waiting before making more requests.');
234
+ logger.consoleLog('\nāŒ Rate limit exceeded. Maximum retries reached.', {
235
+ component: 'OpenAIClient',
236
+ maxRetries
237
+ });
238
+ logger.consoleLog('šŸ’” Tip: Consider upgrading your API plan or waiting before making more requests.');
129
239
  break;
130
240
  }
131
241
  }
@@ -133,34 +243,63 @@ export async function createChatCompletion(client, params, config, messages) {
133
243
  if (isRetryableError(error) && attempt < maxRetries) {
134
244
  const delay = getRetryDelay(attempt);
135
245
  const seconds = Math.round(delay / 1000);
136
- console.log(`\nāš ļø API error (${error?.status || error?.code || 'unknown'}). Retrying in ${seconds} seconds (attempt ${attempt}/${maxRetries})...`);
246
+ logger.consoleLog(`\nāš ļø API error (${error?.status || error?.code || 'unknown'}). Retrying in ${seconds} seconds (attempt ${attempt}/${maxRetries})...`, {
247
+ component: 'OpenAIClient',
248
+ attempt,
249
+ maxRetries,
250
+ errorType: error?.status || error?.code || 'unknown',
251
+ delayMs: delay
252
+ });
137
253
  await sleep(delay);
138
254
  continue;
139
255
  }
140
256
  // For non-retryable errors or max retries reached, break immediately
257
+ logger.error('šŸ’„ Breaking retry loop', {
258
+ component: 'OpenAIClient',
259
+ attempt,
260
+ maxRetries,
261
+ isRetryable: isRetryableError(error)
262
+ });
141
263
  break;
142
264
  }
143
265
  }
144
266
  // If we get here, all retries failed
145
- console.error('\nāŒ API request failed after all retry attempts.');
267
+ logger.consoleLog('\nāŒ API request failed after all retry attempts.', {
268
+ component: 'OpenAIClient',
269
+ maxRetries,
270
+ lastErrorStatus: lastError?.status,
271
+ lastErrorCode: lastError?.code
272
+ });
146
273
  // Provide user-friendly error messages
147
274
  if (lastError?.status === 401) {
148
- console.log('šŸ’” This looks like an authentication error. Check your API key configuration.');
149
- console.log(' Run: protoagent config --update-key');
275
+ logger.consoleLog('šŸ’” This looks like an authentication error. Check your API key configuration.');
276
+ logger.consoleLog(' Run: protoagent config --update-key');
277
+ logger.error('šŸ” Authentication error detected', { component: 'OpenAIClient' });
150
278
  }
151
279
  else if (lastError?.status === 429) {
152
- console.log('šŸ’” Rate limit exceeded. Try again later or upgrade your API plan.');
280
+ logger.consoleLog('šŸ’” Rate limit exceeded. Try again later or upgrade your API plan.');
281
+ logger.error('ā³ Rate limit error detected', { component: 'OpenAIClient' });
153
282
  }
154
283
  else if (lastError?.status === 403) {
155
- console.log('šŸ’” Access forbidden. Check your API key permissions and billing status.');
284
+ logger.consoleLog('šŸ’” Access forbidden. Check your API key permissions and billing status.');
285
+ logger.error('🚫 Forbidden access error detected', { component: 'OpenAIClient' });
156
286
  }
157
287
  else if (lastError?.status >= 500) {
158
- console.log('šŸ’” Server error. The API service may be temporarily unavailable.');
288
+ logger.consoleLog('šŸ’” Server error. The API service may be temporarily unavailable.');
289
+ logger.error('šŸ”§ Server error detected', {
290
+ component: 'OpenAIClient',
291
+ status: lastError.status
292
+ });
159
293
  }
160
294
  else if (lastError?.code === 'ENOTFOUND' || lastError?.message?.includes('network')) {
161
- console.log('šŸ’” Network connection error. Check your internet connection.');
295
+ logger.consoleLog('šŸ’” Network connection error. Check your internet connection.');
296
+ logger.error('🌐 Network error detected', {
297
+ component: 'OpenAIClient',
298
+ code: lastError?.code
299
+ });
162
300
  }
163
301
  // Suggest graceful shutdown
164
- console.log('\n🚪 ProtoAgent will now exit. Please resolve the issue and try again.');
302
+ logger.consoleLog('\n🚪 ProtoAgent will now exit. Please resolve the issue and try again.');
303
+ logger.error('🚪 Exiting due to API failure', { component: 'OpenAIClient' });
165
304
  process.exit(1);
166
305
  }
@@ -7,6 +7,7 @@ import { setupConfig } from './setup.js';
7
7
  import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
8
8
  import fs from 'fs/promises';
9
9
  import path from 'path';
10
+ import { logger } from '../utils/logger.js';
10
11
  /**
11
12
  * Mask API key for display (show first 8 and last 4 characters)
12
13
  */
@@ -26,27 +27,27 @@ export async function showCurrentConfig() {
26
27
  try {
27
28
  const configExists = await hasConfig();
28
29
  if (!configExists) {
29
- console.log('āŒ No configuration found. Run ProtoAgent to set up.');
30
+ logger.consoleLog('āŒ No configuration found. Run ProtoAgent to set up.');
30
31
  return;
31
32
  }
32
33
  const config = await loadConfig();
33
34
  const configDir = getConfigDirectory();
34
- console.log('\nšŸ“‹ Current ProtoAgent Configuration:');
35
- console.log('─'.repeat(40));
36
- console.log(`Provider: ${config.provider}`);
37
- console.log(`Model: ${config.model}`);
35
+ logger.consoleLog('\nšŸ“‹ Current ProtoAgent Configuration:');
36
+ logger.consoleLog('─'.repeat(40));
37
+ logger.consoleLog(`Provider: ${config.provider}`);
38
+ logger.consoleLog(`Model: ${config.model}`);
38
39
  // Show the appropriate API key based on provider
39
40
  if (config.provider === 'openai' && config.credentials.OPENAI_API_KEY) {
40
- console.log(`API Key: ${maskApiKey(config.credentials.OPENAI_API_KEY)}`);
41
+ logger.consoleLog(`API Key: ${maskApiKey(config.credentials.OPENAI_API_KEY)}`);
41
42
  }
42
43
  else if (config.provider === 'gemini' && config.credentials.GEMINI_API_KEY) {
43
- console.log(`API Key: ${maskApiKey(config.credentials.GEMINI_API_KEY)}`);
44
+ logger.consoleLog(`API Key: ${maskApiKey(config.credentials.GEMINI_API_KEY)}`);
44
45
  }
45
46
  else if (config.provider === 'cerebras' && config.credentials.CEREBRAS_API_KEY) {
46
- console.log(`API Key: ${maskApiKey(config.credentials.CEREBRAS_API_KEY)}`);
47
+ logger.consoleLog(`API Key: ${maskApiKey(config.credentials.CEREBRAS_API_KEY)}`);
47
48
  }
48
- console.log(`Config Location: ${configDir}/config.json`);
49
- console.log('─'.repeat(40));
49
+ logger.consoleLog(`Config Location: ${configDir}/config.json`);
50
+ logger.consoleLog('─'.repeat(40));
50
51
  }
51
52
  catch (error) {
52
53
  console.error(`āŒ Error reading configuration: ${error.message}`);
@@ -59,11 +60,11 @@ export async function updateApiKey() {
59
60
  try {
60
61
  const configExists = await hasConfig();
61
62
  if (!configExists) {
62
- console.log('āŒ No configuration found. Run ProtoAgent to set up first.');
63
+ logger.consoleLog('āŒ No configuration found. Run ProtoAgent to set up first.');
63
64
  return;
64
65
  }
65
66
  const config = await loadConfig();
66
- console.log(`\nšŸ”‘ Update ${config.provider.toUpperCase()} API Key`);
67
+ logger.consoleLog(`\nšŸ”‘ Update ${config.provider.toUpperCase()} API Key`);
67
68
  // Show current API key based on provider
68
69
  let currentKey = '';
69
70
  let helpUrl = '';
@@ -83,8 +84,8 @@ export async function updateApiKey() {
83
84
  helpUrl = 'https://cloud.cerebras.ai/platform';
84
85
  keyPrefix = '';
85
86
  }
86
- console.log(`Current API Key: ${maskApiKey(currentKey)}`);
87
- console.log(`Get your API key from: ${helpUrl}\n`);
87
+ logger.consoleLog(`Current API Key: ${maskApiKey(currentKey)}`);
88
+ logger.consoleLog(`Get your API key from: ${helpUrl}\n`);
88
89
  const { newApiKey } = await inquirer.prompt([
89
90
  {
90
91
  type: 'password',
@@ -113,8 +114,8 @@ export async function updateApiKey() {
113
114
  config.credentials.CEREBRAS_API_KEY = newApiKey.trim();
114
115
  }
115
116
  await saveConfig(config);
116
- console.log('\nāœ… API key updated successfully!');
117
- console.log(`New API Key: ${maskApiKey(newApiKey)}`);
117
+ logger.consoleLog('\nāœ… API key updated successfully!');
118
+ logger.consoleLog(`New API Key: ${maskApiKey(newApiKey)}`);
118
119
  }
119
120
  catch (error) {
120
121
  console.error(`āŒ Error updating API key: ${error.message}`);
@@ -127,12 +128,12 @@ export async function updateModel() {
127
128
  try {
128
129
  const configExists = await hasConfig();
129
130
  if (!configExists) {
130
- console.log('āŒ No configuration found. Run ProtoAgent to set up first.');
131
+ logger.consoleLog('āŒ No configuration found. Run ProtoAgent to set up first.');
131
132
  return;
132
133
  }
133
134
  const config = await loadConfig();
134
- console.log(`\nšŸ¤– Update ${config.provider.toUpperCase()} Model`);
135
- console.log(`Current Model: ${config.model}\n`);
135
+ logger.consoleLog(`\nšŸ¤– Update ${config.provider.toUpperCase()} Model`);
136
+ logger.consoleLog(`Current Model: ${config.model}\n`);
136
137
  // Get available models based on provider
137
138
  let availableModels = [];
138
139
  if (config.provider === 'openai') {
@@ -157,16 +158,16 @@ export async function updateModel() {
157
158
  }
158
159
  ]);
159
160
  if (newModel === config.model) {
160
- console.log('✨ No change needed - same model selected.');
161
+ logger.consoleLog('✨ No change needed - same model selected.');
161
162
  return;
162
163
  }
163
164
  // Update configuration
164
165
  const previousModel = config.model;
165
166
  config.model = newModel;
166
167
  await saveConfig(config);
167
- console.log('\nāœ… Model updated successfully!');
168
- console.log(`Previous Model: ${previousModel}`);
169
- console.log(`New Model: ${newModel}`);
168
+ logger.consoleLog('\nāœ… Model updated successfully!');
169
+ logger.consoleLog(`Previous Model: ${previousModel}`);
170
+ logger.consoleLog(`New Model: ${newModel}`);
170
171
  }
171
172
  catch (error) {
172
173
  console.error(`āŒ Error updating model: ${error.message}`);
@@ -179,8 +180,8 @@ export async function resetConfiguration() {
179
180
  try {
180
181
  const configExists = await hasConfig();
181
182
  if (configExists) {
182
- console.log('\nāš ļø Reset Configuration');
183
- console.log('This will delete your current configuration and set up ProtoAgent from scratch.');
183
+ logger.consoleLog('\nāš ļø Reset Configuration');
184
+ logger.consoleLog('This will delete your current configuration and set up ProtoAgent from scratch.');
184
185
  const { confirm } = await inquirer.prompt([
185
186
  {
186
187
  type: 'confirm',
@@ -190,16 +191,16 @@ export async function resetConfiguration() {
190
191
  }
191
192
  ]);
192
193
  if (!confirm) {
193
- console.log('Configuration reset cancelled.');
194
+ logger.consoleLog('Configuration reset cancelled.');
194
195
  return;
195
196
  }
196
197
  // Delete existing configuration
197
198
  const configPath = path.join(getConfigDirectory(), 'config.json');
198
199
  await fs.unlink(configPath);
199
- console.log('āœ… Existing configuration deleted.');
200
+ logger.consoleLog('āœ… Existing configuration deleted.');
200
201
  }
201
202
  // Run setup again
202
- console.log('\nšŸ”„ Starting fresh configuration setup...');
203
+ logger.consoleLog('\nšŸ”„ Starting fresh configuration setup...');
203
204
  await setupConfig();
204
205
  }
205
206
  catch (error) {
@@ -4,12 +4,13 @@
4
4
  import inquirer from 'inquirer';
5
5
  import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
6
6
  import { saveConfig, getConfigDirectory } from './manager.js';
7
+ import { logger } from '../utils/logger.js';
7
8
  /**
8
9
  * Run interactive configuration setup
9
10
  */
10
11
  export async function setupConfig() {
11
- console.log('\nšŸ¤– Welcome to ProtoAgent!');
12
- console.log("Let's set up your model configuration.\n");
12
+ logger.consoleLog('\nšŸ¤– Welcome to ProtoAgent!');
13
+ logger.consoleLog("Let's set up your model configuration.\n");
13
14
  // Step 1: Select provider and model
14
15
  const modelChoices = [
15
16
  new inquirer.Separator('--- OpenAI Models ---'),
@@ -47,9 +48,9 @@ export async function setupConfig() {
47
48
  CEREBRAS_API_KEY: ''
48
49
  };
49
50
  if (provider === 'openai') {
50
- console.log(`\nšŸ“ ${openaiProvider.auth.label} Setup`);
51
- console.log("You'll need an API key from OpenAI.");
52
- console.log(`Get your API key from: ${openaiProvider.auth.helpUrl}`);
51
+ logger.consoleLog(`\nšŸ“ ${openaiProvider.auth.label} Setup`);
52
+ logger.consoleLog("You'll need an API key from OpenAI.");
53
+ logger.consoleLog(`Get your API key from: ${openaiProvider.auth.helpUrl}`);
53
54
  const response = await inquirer.prompt([
54
55
  {
55
56
  type: 'password',
@@ -71,9 +72,9 @@ export async function setupConfig() {
71
72
  credentials.OPENAI_API_KEY = apiKey;
72
73
  }
73
74
  else if (provider === 'gemini') {
74
- console.log(`\nšŸ“ ${geminiProvider.auth.label} Setup`);
75
- console.log("You'll need an API key from Gemini.");
76
- console.log(`Get your API key from: ${geminiProvider.auth.helpUrl}`);
75
+ logger.consoleLog(`\nšŸ“ ${geminiProvider.auth.label} Setup`);
76
+ logger.consoleLog("You'll need an API key from Gemini.");
77
+ logger.consoleLog(`Get your API key from: ${geminiProvider.auth.helpUrl}`);
77
78
  const response = await inquirer.prompt([
78
79
  {
79
80
  type: 'password',
@@ -93,9 +94,9 @@ export async function setupConfig() {
93
94
  credentials.GEMINI_API_KEY = apiKey;
94
95
  }
95
96
  else if (provider === 'cerebras') {
96
- console.log(`\nšŸ“ ${cerebrasProvider.auth.label} Setup`);
97
- console.log("You'll need an API key from Cerebras.");
98
- console.log(`Get your API key from: ${cerebrasProvider.auth.helpUrl}`);
97
+ logger.consoleLog(`\nšŸ“ ${cerebrasProvider.auth.label} Setup`);
98
+ logger.consoleLog("You'll need an API key from Cerebras.");
99
+ logger.consoleLog(`Get your API key from: ${cerebrasProvider.auth.helpUrl}`);
99
100
  const response = await inquirer.prompt([
100
101
  {
101
102
  type: 'password',
@@ -115,7 +116,7 @@ export async function setupConfig() {
115
116
  credentials.CEREBRAS_API_KEY = apiKey;
116
117
  }
117
118
  // Step 3: Confirm configuration
118
- console.log('\nšŸ“‹ Configuration Summary:');
119
+ logger.consoleLog('\nšŸ“‹ Configuration Summary:');
119
120
  let providerName = '';
120
121
  if (provider === 'openai') {
121
122
  providerName = openaiProvider.name;
@@ -126,9 +127,9 @@ export async function setupConfig() {
126
127
  else if (provider === 'cerebras') {
127
128
  providerName = cerebrasProvider.name;
128
129
  }
129
- console.log(`Provider: ${providerName}`);
130
- console.log(`Model: ${model}`);
131
- console.log(`API Key: ${'*'.repeat(Math.min(apiKey.length, 20))}...`);
130
+ logger.consoleLog(`Provider: ${providerName}`);
131
+ logger.consoleLog(`Model: ${model}`);
132
+ logger.consoleLog(`API Key: ${'*'.repeat(Math.min(apiKey.length, 20))}...`);
132
133
  const { confirm } = await inquirer.prompt([
133
134
  {
134
135
  type: 'confirm',
@@ -138,7 +139,7 @@ export async function setupConfig() {
138
139
  }
139
140
  ]);
140
141
  if (!confirm) {
141
- console.log('Configuration cancelled.');
142
+ logger.consoleLog('Configuration cancelled.');
142
143
  process.exit(0);
143
144
  }
144
145
  // Step 4: Save configuration
@@ -149,9 +150,9 @@ export async function setupConfig() {
149
150
  };
150
151
  try {
151
152
  await saveConfig(config);
152
- console.log(`\nāœ… Configuration saved successfully!`);
153
- console.log(`Config location: ${getConfigDirectory()}/config.json`);
154
- console.log("\nYou're all set! ProtoAgent is ready to use.\n");
153
+ logger.consoleLog(`\nāœ… Configuration saved successfully!`);
154
+ logger.consoleLog(`Config location: ${getConfigDirectory()}/config.json`);
155
+ logger.consoleLog("\nYou're all set! ProtoAgent is ready to use.\n");
155
156
  return config;
156
157
  }
157
158
  catch (error) {
@@ -9,6 +9,7 @@ import { searchFilesTool } from '../tools/search-files.js';
9
9
  import { runShellCommandTool } from '../tools/run-shell-command.js';
10
10
  import fs from 'fs/promises';
11
11
  import path from 'path';
12
+ import { logger } from '../utils/logger.js';
12
13
  // Collect all tools
13
14
  const allTools = [
14
15
  readFileTool,
@@ -82,10 +83,23 @@ async function generateDirectoryTree(dirPath = '.', depth = 0, maxDepth = 3) {
82
83
  }
83
84
  // Generate project context asynchronously
84
85
  async function generateProjectContext() {
86
+ logger.debug('šŸ“ Generating project context', { component: 'SystemPrompt' });
85
87
  const workingDir = getCurrentWorkingDirectory();
86
88
  const projectName = path.basename(workingDir);
89
+ logger.debug('šŸ“‚ Project info', {
90
+ component: 'SystemPrompt',
91
+ workingDir,
92
+ projectName
93
+ });
87
94
  try {
95
+ const treeStartTime = Date.now();
88
96
  const tree = await generateDirectoryTree();
97
+ const treeGenerationTime = Date.now() - treeStartTime;
98
+ logger.debug('🌳 Directory tree generated', {
99
+ component: 'SystemPrompt',
100
+ treeLength: tree.length,
101
+ generationTime: treeGenerationTime
102
+ });
89
103
  return `
90
104
  ## Current Project Context:
91
105
 
@@ -96,6 +110,10 @@ async function generateProjectContext() {
96
110
  ${tree}`;
97
111
  }
98
112
  catch (error) {
113
+ logger.error('āŒ Failed to generate directory tree', {
114
+ component: 'SystemPrompt',
115
+ error: error instanceof Error ? error.message : String(error)
116
+ });
99
117
  return `
100
118
  ## Current Project Context:
101
119
 
@@ -107,7 +125,15 @@ ${tree}`;
107
125
  }
108
126
  // Generate the complete system prompt with project context
109
127
  export async function generateSystemPrompt() {
128
+ logger.debug('šŸ—ļø Generating system prompt', { component: 'SystemPrompt' });
129
+ const startTime = Date.now();
110
130
  const projectContext = await generateProjectContext();
131
+ const generationTime = Date.now() - startTime;
132
+ logger.debug('āœ… System prompt generated', {
133
+ component: 'SystemPrompt',
134
+ generationTime,
135
+ contextLength: projectContext.length
136
+ });
111
137
  return `You are ProtoAgent, a helpful coding assistant with file system and shell command capabilities. Your objective is to help the user
112
138
  complete their coding tasks, most likely related to the directory you were started in.
113
139
 
@@ -131,6 +157,24 @@ When users ask you to work with files or run commands, you should:
131
157
  - Show the user the results of your operations
132
158
  - Ask for confirmation before running any write, delete operations
133
159
 
160
+ ## CRITICAL: Always Be Proactive With Tool Usage
161
+
162
+ **When users ask about existing code, projects, or codebases:**
163
+ 1. IMMEDIATELY start using tools to explore and understand the codebase
164
+ 2. Use find/search commands to locate relevant files and directories
165
+ 3. Use read_file to examine source code files
166
+ 4. Use list_directory and view_directory_tree to understand project structure
167
+ 5. NEVER just "think" about code - always examine it with tools first
168
+
169
+ **When asked to analyze or compare codebases:**
170
+ 1. Start by searching for the mentioned projects/directories
171
+ 2. Explore their structure systematically
172
+ 3. Read key files (main entry points, package.json, README, etc.)
173
+ 4. Build understanding iteratively through tool usage
174
+ 5. Document findings as you discover them
175
+
176
+ **MANDATORY: If a user mentions specific codebases or asks you to analyze source code, you MUST start using tools immediately to explore and understand the code before providing any analysis.**
177
+
134
178
  ## TODO TRACKING - MANDATORY REQUIREMENT:
135
179
 
136
180
  **YOU MUST ALWAYS USE TODO TRACKING FOR ANY NON-TRIVIAL TASK:**
@@ -244,6 +288,24 @@ When users ask you to work with files or run commands, you should:
244
288
  - Show the user the results of your operations
245
289
  - Ask for confirmation before running any write, delete operations
246
290
 
291
+ ## CRITICAL: Always Be Proactive With Tool Usage
292
+
293
+ **When users ask about existing code, projects, or codebases:**
294
+ 1. IMMEDIATELY start using tools to explore and understand the codebase
295
+ 2. Use find/search commands to locate relevant files and directories
296
+ 3. Use read_file to examine source code files
297
+ 4. Use list_directory and view_directory_tree to understand project structure
298
+ 5. NEVER just "think" about code - always examine it with tools first
299
+
300
+ **When asked to analyze or compare codebases:**
301
+ 1. Start by searching for the mentioned projects/directories
302
+ 2. Explore their structure systematically
303
+ 3. Read key files (main entry points, package.json, README, etc.)
304
+ 4. Build understanding iteratively through tool usage
305
+ 5. Document findings as you discover them
306
+
307
+ **MANDATORY: If a user mentions specific codebases or asks you to analyze source code, you MUST start using tools immediately to explore and understand the code before providing any analysis.**
308
+
247
309
  ## File Operation Guidelines - CRITICAL:
248
310
 
249
311
  **ALWAYS PREFER EDITING OVER WRITING FILES:**