prab-cli 1.1.0 → 1.2.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.
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ const groq_models_1 = require("./lib/groq-models");
60
60
  // Import chat handler and safety
61
61
  const chat_handler_1 = require("./lib/chat-handler");
62
62
  const safety_1 = require("./lib/safety");
63
+ const chalk_1 = __importDefault(require("chalk"));
63
64
  const tracker_1 = require("./lib/tracker");
64
65
  // Cache for available models
65
66
  let cachedModels = [];
@@ -370,7 +371,90 @@ program.action(async () => {
370
371
  continue;
371
372
  }
372
373
  // Process user input with chat handler
373
- await chatHandler.processUserInput(userInput);
374
+ const result = await chatHandler.processUserInput(userInput);
375
+ // Handle model errors - offer to switch models
376
+ if (!result.success && result.isModelError) {
377
+ console.log("");
378
+ console.log(chalk_1.default.yellow("┌─────────────────────────────────────────────────────┐"));
379
+ console.log(chalk_1.default.yellow("│") +
380
+ chalk_1.default.red.bold(" Model Error Detected ") +
381
+ chalk_1.default.yellow("│"));
382
+ console.log(chalk_1.default.yellow("└─────────────────────────────────────────────────────┘"));
383
+ const errorMessages = {
384
+ rate_limit: "Rate limit exceeded. The model is receiving too many requests.",
385
+ model_unavailable: "Model is currently unavailable or overloaded.",
386
+ auth_error: "Authentication error. Please check your API key.",
387
+ unknown: "An error occurred with the current model.",
388
+ };
389
+ console.log(chalk_1.default.dim(` ${errorMessages[result.errorType || "unknown"]}`));
390
+ console.log("");
391
+ console.log(chalk_1.default.cyan(" Would you like to switch to a different model?"));
392
+ console.log("");
393
+ // Fetch models if not cached
394
+ if (cachedModels.length === 0) {
395
+ const spinner = (0, ora_1.default)("Fetching available models...").start();
396
+ cachedModels = await (0, groq_models_1.fetchGroqModels)(apiKey);
397
+ spinner.succeed(`Found ${cachedModels.length} models`);
398
+ }
399
+ // Filter out the current model and build choices
400
+ const currentModel = modelProvider.modelId;
401
+ const availableModels = cachedModels.filter((m) => m.id !== currentModel);
402
+ const grouped = (0, groq_models_1.groupModelsByOwner)(availableModels);
403
+ const modelChoices = [];
404
+ // Add "Cancel" option first
405
+ modelChoices.push({
406
+ name: chalk_1.default.dim(" Cancel (keep current model)"),
407
+ value: "__cancel__",
408
+ });
409
+ modelChoices.push(new select_1.Separator("─── Available Models ───"));
410
+ for (const [owner, models] of grouped) {
411
+ modelChoices.push(new select_1.Separator(`─── ${owner} ───`));
412
+ for (const model of models) {
413
+ const ctx = (0, groq_models_1.formatContextWindow)(model.context_window);
414
+ modelChoices.push({
415
+ name: ` ${model.id} (${ctx} ctx)`,
416
+ value: model.id,
417
+ });
418
+ }
419
+ }
420
+ try {
421
+ const selectedModel = await (0, select_1.default)({
422
+ message: "Select a model to switch to:",
423
+ choices: modelChoices,
424
+ pageSize: 12,
425
+ loop: false,
426
+ });
427
+ if (selectedModel && selectedModel !== "__cancel__") {
428
+ const oldModel = currentModel;
429
+ (0, config_1.setActiveModel)(selectedModel);
430
+ modelProvider.setModel(selectedModel);
431
+ chatHandler.updateModelProvider(modelProvider);
432
+ ui_1.log.success(`Switched to model: ${selectedModel}`);
433
+ tracker_1.tracker.modelSwitch(oldModel, selectedModel, true);
434
+ // Ask if user wants to retry the last message
435
+ console.log("");
436
+ const { retry } = await inquirer_1.default.prompt([
437
+ {
438
+ type: "confirm",
439
+ name: "retry",
440
+ message: "Retry your last message with the new model?",
441
+ default: true,
442
+ },
443
+ ]);
444
+ if (retry) {
445
+ console.log(chalk_1.default.dim("Retrying with new model..."));
446
+ await chatHandler.processUserInput(userInput);
447
+ }
448
+ }
449
+ else {
450
+ ui_1.log.info("Keeping current model. You can try again or switch models with /model");
451
+ }
452
+ }
453
+ catch (e) {
454
+ // User cancelled with Ctrl+C
455
+ ui_1.log.info("Model switch cancelled.");
456
+ }
457
+ }
374
458
  }
375
459
  });
376
460
  program.parse(process.argv);
@@ -11,7 +11,7 @@ const tracker_1 = require("./tracker");
11
11
  class ChatHandler {
12
12
  constructor(toolRegistry, toolExecutor, modelProvider, initialContext) {
13
13
  this.messages = [];
14
- this.contextMessage = '';
14
+ this.contextMessage = "";
15
15
  this.usage = {
16
16
  promptTokens: 0,
17
17
  completionTokens: 0,
@@ -58,9 +58,9 @@ When you need to perform file operations, use the appropriate tools rather than
58
58
  try {
59
59
  // Auto-attach file context if files are mentioned
60
60
  const attachedFiles = await this.attachMentionedFiles(input);
61
- let finalInput = input;
61
+ const finalInput = input;
62
62
  if (attachedFiles.length > 0) {
63
- ui_1.log.info(`Attached context for: ${attachedFiles.join(', ')}`);
63
+ ui_1.log.info(`Attached context for: ${attachedFiles.join(", ")}`);
64
64
  tracker_1.tracker.contextAttached(attachedFiles);
65
65
  }
66
66
  // Add user message
@@ -73,20 +73,20 @@ When you need to perform file operations, use the appropriate tools rather than
73
73
  const maxIterations = 10; // Prevent infinite loops
74
74
  while (continueLoop && iterationCount < maxIterations) {
75
75
  iterationCount++;
76
- tracker_1.tracker.iteration(iterationCount, 'Starting API call');
76
+ tracker_1.tracker.iteration(iterationCount, "Starting API call");
77
77
  // Log API request
78
78
  tracker_1.tracker.apiRequest(this.modelProvider.modelId, this.messages.length, tools.length);
79
79
  const apiStartTime = Date.now();
80
80
  // Stream response from model
81
81
  const stream = this.modelProvider.streamChat(this.messages, tools);
82
- let fullResponse = '';
82
+ let fullResponse = "";
83
83
  let toolCalls = [];
84
84
  const formatter = new ui_1.StreamFormatter();
85
- process.stdout.write('\n');
85
+ process.stdout.write("\n");
86
86
  try {
87
87
  for await (const chunk of stream) {
88
88
  // Handle text content
89
- if (chunk.content && typeof chunk.content === 'string') {
89
+ if (chunk.content && typeof chunk.content === "string") {
90
90
  fullResponse += chunk.content;
91
91
  // Format and output the chunk with syntax highlighting
92
92
  const formatted = formatter.processChunk(chunk.content);
@@ -120,7 +120,7 @@ When you need to perform file operations, use the appropriate tools rather than
120
120
  tracker_1.tracker.apiError(apiError.message, { stack: apiError.stack });
121
121
  throw apiError;
122
122
  }
123
- process.stdout.write('\n\n');
123
+ process.stdout.write("\n\n");
124
124
  // Log AI response if there's content
125
125
  if (fullResponse.length > 0) {
126
126
  tracker_1.tracker.aiResponse(fullResponse);
@@ -128,14 +128,14 @@ When you need to perform file operations, use the appropriate tools rather than
128
128
  // If we have tool calls, execute them
129
129
  if (toolCalls.length > 0) {
130
130
  // Log AI's tool decision
131
- tracker_1.tracker.aiToolDecision(toolCalls.map(tc => ({
131
+ tracker_1.tracker.aiToolDecision(toolCalls.map((tc) => ({
132
132
  name: tc.name,
133
- args: tc.args || {}
133
+ args: tc.args || {},
134
134
  })));
135
135
  // Add AI message with tool calls
136
136
  this.messages.push(new messages_1.AIMessage({
137
137
  content: fullResponse,
138
- tool_calls: toolCalls
138
+ tool_calls: toolCalls,
139
139
  }));
140
140
  // Execute tools
141
141
  const results = await this.executeToolCalls(toolCalls);
@@ -146,10 +146,10 @@ When you need to perform file operations, use the appropriate tools rather than
146
146
  this.messages.push(new messages_1.ToolMessage({
147
147
  content: result.success ? result.output : `Error: ${result.error}`,
148
148
  tool_call_id: toolCall.id,
149
- name: toolCall.name
149
+ name: toolCall.name,
150
150
  }));
151
151
  }
152
- tracker_1.tracker.iteration(iterationCount, 'Tool execution complete, continuing loop');
152
+ tracker_1.tracker.iteration(iterationCount, "Tool execution complete, continuing loop");
153
153
  // Continue loop to get AI's response after tool execution
154
154
  continue;
155
155
  }
@@ -157,31 +157,98 @@ When you need to perform file operations, use the appropriate tools rather than
157
157
  // No tool calls, add final AI message and end loop
158
158
  this.messages.push(new messages_1.AIMessage(fullResponse));
159
159
  continueLoop = false;
160
- tracker_1.tracker.iteration(iterationCount, 'No tool calls, ending loop');
160
+ tracker_1.tracker.iteration(iterationCount, "No tool calls, ending loop");
161
161
  }
162
162
  }
163
163
  if (iterationCount >= maxIterations) {
164
- ui_1.log.warning('Maximum iteration limit reached. Ending conversation turn.');
165
- tracker_1.tracker.warn('Max iterations reached', { iterations: iterationCount });
164
+ ui_1.log.warning("Maximum iteration limit reached. Ending conversation turn.");
165
+ tracker_1.tracker.warn("Max iterations reached", { iterations: iterationCount });
166
166
  }
167
167
  // Log success
168
168
  const duration = Date.now() - startTime;
169
169
  tracker_1.tracker.promptComplete(input, duration, iterationCount);
170
+ return { success: true };
170
171
  }
171
172
  catch (error) {
172
- ui_1.log.error(`Error: ${error.message}`);
173
- tracker_1.tracker.promptFailed(input, error.message);
174
- tracker_1.tracker.error('Chat processing failed', error);
173
+ const errorMessage = error.message || "Unknown error";
174
+ ui_1.log.error(`Error: ${errorMessage}`);
175
+ tracker_1.tracker.promptFailed(input, errorMessage);
176
+ tracker_1.tracker.error("Chat processing failed", error);
177
+ // Detect model-specific errors
178
+ const isModelError = this.isModelError(errorMessage);
179
+ const errorType = this.classifyError(errorMessage);
180
+ if (isModelError) {
181
+ // Remove the user message so they can retry with a different model
182
+ this.messages.pop();
183
+ }
184
+ return {
185
+ success: false,
186
+ error: errorMessage,
187
+ isModelError,
188
+ errorType,
189
+ };
190
+ }
191
+ }
192
+ /**
193
+ * Check if error is related to the model (rate limit, unavailable, etc.)
194
+ */
195
+ isModelError(errorMessage) {
196
+ const modelErrorPatterns = [
197
+ "rate limit",
198
+ "rate_limit",
199
+ "quota exceeded",
200
+ "model not found",
201
+ "model is not available",
202
+ "model_not_available",
203
+ "overloaded",
204
+ "capacity",
205
+ "too many requests",
206
+ "429",
207
+ "503",
208
+ "502",
209
+ "service unavailable",
210
+ "timeout",
211
+ "context length",
212
+ "maximum context",
213
+ "token limit",
214
+ ];
215
+ const lowerMessage = errorMessage.toLowerCase();
216
+ return modelErrorPatterns.some((pattern) => lowerMessage.includes(pattern));
217
+ }
218
+ /**
219
+ * Classify the type of error
220
+ */
221
+ classifyError(errorMessage) {
222
+ const lowerMessage = errorMessage.toLowerCase();
223
+ if (lowerMessage.includes("rate limit") ||
224
+ lowerMessage.includes("rate_limit") ||
225
+ lowerMessage.includes("too many requests") ||
226
+ lowerMessage.includes("429")) {
227
+ return "rate_limit";
228
+ }
229
+ if (lowerMessage.includes("model not found") ||
230
+ lowerMessage.includes("not available") ||
231
+ lowerMessage.includes("overloaded") ||
232
+ lowerMessage.includes("503") ||
233
+ lowerMessage.includes("502")) {
234
+ return "model_unavailable";
235
+ }
236
+ if (lowerMessage.includes("auth") ||
237
+ lowerMessage.includes("api key") ||
238
+ lowerMessage.includes("unauthorized") ||
239
+ lowerMessage.includes("401")) {
240
+ return "auth_error";
175
241
  }
242
+ return "unknown";
176
243
  }
177
244
  /**
178
245
  * Execute tool calls from AI
179
246
  */
180
247
  async executeToolCalls(toolCalls) {
181
- const formattedCalls = toolCalls.map(tc => ({
248
+ const formattedCalls = toolCalls.map((tc) => ({
182
249
  id: tc.id || `call-${Date.now()}`,
183
250
  name: tc.name,
184
- args: tc.args || {}
251
+ args: tc.args || {},
185
252
  }));
186
253
  return await this.toolExecutor.executeMultiple(formattedCalls);
187
254
  }
@@ -192,7 +259,7 @@ When you need to perform file operations, use the appropriate tools rather than
192
259
  const allFiles = await (0, context_1.getFileTree)();
193
260
  const attached = [];
194
261
  for (const file of allFiles) {
195
- const base = file.split('/').pop() || '';
262
+ const base = file.split("/").pop() || "";
196
263
  const isBaseMatch = base.length > 2 && input.includes(base);
197
264
  const isPathMatch = input.includes(file);
198
265
  if (isPathMatch || isBaseMatch) {
@@ -211,7 +278,7 @@ When you need to perform file operations, use the appropriate tools rather than
211
278
  clearHistory() {
212
279
  const systemMsg = this.messages[0];
213
280
  this.messages = [systemMsg];
214
- tracker_1.tracker.debug('Chat history cleared');
281
+ tracker_1.tracker.debug("Chat history cleared");
215
282
  }
216
283
  /**
217
284
  * Get message count
@@ -235,5 +302,23 @@ When you need to perform file operations, use the appropriate tools rather than
235
302
  this.messages[0] = systemMsg; // Keep the same message for now
236
303
  // In a more sophisticated implementation, we'd rebuild the system message
237
304
  }
305
+ /**
306
+ * Update the model provider (for switching models)
307
+ */
308
+ updateModelProvider(modelProvider) {
309
+ this.modelProvider = modelProvider;
310
+ }
311
+ /**
312
+ * Get the last user input (for retry after model switch)
313
+ */
314
+ getLastUserInput() {
315
+ for (let i = this.messages.length - 1; i >= 0; i--) {
316
+ const msg = this.messages[i];
317
+ if (msg instanceof messages_1.HumanMessage) {
318
+ return msg.content;
319
+ }
320
+ }
321
+ return null;
322
+ }
238
323
  }
239
324
  exports.ChatHandler = ChatHandler;
@@ -8,8 +8,8 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const os_1 = __importDefault(require("os"));
10
10
  const registry_1 = require("./models/registry");
11
- const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.config', 'groq-cli-tool');
12
- const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
11
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".config", "groq-cli-tool");
12
+ const CONFIG_FILE = path_1.default.join(CONFIG_DIR, "config.json");
13
13
  const ensureConfigDir = () => {
14
14
  if (!fs_1.default.existsSync(CONFIG_DIR)) {
15
15
  fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
@@ -19,7 +19,7 @@ const readConfig = () => {
19
19
  try {
20
20
  if (!fs_1.default.existsSync(CONFIG_FILE))
21
21
  return {};
22
- return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, 'utf-8'));
22
+ return JSON.parse(fs_1.default.readFileSync(CONFIG_FILE, "utf-8"));
23
23
  }
24
24
  catch {
25
25
  return {};
@@ -35,22 +35,22 @@ const writeConfig = (data) => {
35
35
  const getConfig = () => {
36
36
  const config = readConfig();
37
37
  // Handle legacy config format (old apiKey -> new apiKeys.groq)
38
- if ('apiKey' in config && typeof config.apiKey === 'string') {
38
+ if ("apiKey" in config && typeof config.apiKey === "string") {
39
39
  const legacyKey = config.apiKey;
40
40
  delete config.apiKey;
41
41
  config.apiKeys = { groq: legacyKey };
42
42
  writeConfig(config);
43
43
  }
44
44
  return {
45
- apiKeys: config.apiKeys || { groq: '' },
45
+ apiKeys: config.apiKeys || { groq: "" },
46
46
  activeModel: config.activeModel || (0, registry_1.getDefaultModel)(),
47
47
  preferences: {
48
48
  temperature: config.preferences?.temperature ?? 0.7,
49
49
  autoConfirm: config.preferences?.autoConfirm ?? false,
50
50
  safeMode: config.preferences?.safeMode ?? true,
51
- maxTokens: config.preferences?.maxTokens
51
+ maxTokens: config.preferences?.maxTokens,
52
52
  },
53
- session: config.session || { todos: [] }
53
+ session: config.session || { todos: [] },
54
54
  };
55
55
  };
56
56
  exports.getConfig = getConfig;
@@ -59,7 +59,7 @@ exports.getConfig = getConfig;
59
59
  */
60
60
  const getApiKey = () => {
61
61
  const config = (0, exports.getConfig)();
62
- return config.apiKeys.groq || '';
62
+ return config.apiKeys.groq || "";
63
63
  };
64
64
  exports.getApiKey = getApiKey;
65
65
  /**
@@ -68,7 +68,7 @@ exports.getApiKey = getApiKey;
68
68
  const setApiKey = (key) => {
69
69
  const config = readConfig();
70
70
  if (!config.apiKeys) {
71
- config.apiKeys = { groq: '' };
71
+ config.apiKeys = { groq: "" };
72
72
  }
73
73
  config.apiKeys.groq = key;
74
74
  writeConfig(config);
@@ -80,7 +80,7 @@ exports.setApiKey = setApiKey;
80
80
  const clearApiKey = () => {
81
81
  const config = readConfig();
82
82
  if (config.apiKeys) {
83
- config.apiKeys.groq = '';
83
+ config.apiKeys.groq = "";
84
84
  }
85
85
  writeConfig(config);
86
86
  };
@@ -92,7 +92,7 @@ const getModelConfig = () => {
92
92
  const config = (0, exports.getConfig)();
93
93
  return {
94
94
  modelId: config.activeModel,
95
- temperature: config.preferences.temperature
95
+ temperature: config.preferences.temperature,
96
96
  };
97
97
  };
98
98
  exports.getModelConfig = getModelConfig;
@@ -121,7 +121,7 @@ const setPreference = (key, value) => {
121
121
  config.preferences = {
122
122
  temperature: 0.7,
123
123
  autoConfirm: false,
124
- safeMode: true
124
+ safeMode: true,
125
125
  };
126
126
  }
127
127
  config.preferences[key] = value;
@@ -12,33 +12,28 @@ const isGitRepo = async () => {
12
12
  try {
13
13
  return await git.checkIsRepo();
14
14
  }
15
- catch (e) {
15
+ catch {
16
16
  return false;
17
17
  }
18
18
  };
19
19
  exports.isGitRepo = isGitRepo;
20
20
  const getFileTree = async (cwd = process.cwd()) => {
21
21
  // Ignore node_modules, .git, dist, etc.
22
- const ignore = ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/.env', '**/*.lock'];
23
- const files = await (0, glob_1.glob)('**/*', { cwd, ignore, nodir: true });
22
+ const ignore = ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/.env", "**/*.lock"];
23
+ const files = await (0, glob_1.glob)("**/*", { cwd, ignore, nodir: true });
24
24
  return files;
25
25
  };
26
26
  exports.getFileTree = getFileTree;
27
27
  const getFileContent = (filePath) => {
28
28
  try {
29
- return fs_1.default.readFileSync(filePath, 'utf-8');
29
+ return fs_1.default.readFileSync(filePath, "utf-8");
30
30
  }
31
- catch (e) {
32
- return '';
31
+ catch {
32
+ return "";
33
33
  }
34
34
  };
35
35
  exports.getFileContent = getFileContent;
36
36
  const writeFile = (filePath, content) => {
37
- try {
38
- fs_1.default.writeFileSync(filePath, content, 'utf-8');
39
- }
40
- catch (e) {
41
- throw e;
42
- }
37
+ fs_1.default.writeFileSync(filePath, content, "utf-8");
43
38
  };
44
39
  exports.writeFile = writeFile;
@@ -16,12 +16,12 @@ async function fetchGroqModels(apiKey) {
16
16
  const response = await groq.models.list();
17
17
  // Filter and sort models
18
18
  const models = response.data
19
- .filter(m => m.active !== false)
19
+ .filter((m) => m.active !== false)
20
20
  .sort((a, b) => a.id.localeCompare(b.id));
21
21
  return models;
22
22
  }
23
23
  catch (error) {
24
- console.error('Failed to fetch models:', error.message);
24
+ console.error("Failed to fetch models:", error.message);
25
25
  return [];
26
26
  }
27
27
  }
@@ -31,7 +31,7 @@ async function fetchGroqModels(apiKey) {
31
31
  function groupModelsByOwner(models) {
32
32
  const groups = new Map();
33
33
  for (const model of models) {
34
- const owner = model.owned_by || 'Other';
34
+ const owner = model.owned_by || "Other";
35
35
  if (!groups.has(owner)) {
36
36
  groups.set(owner, []);
37
37
  }
package/dist/lib/groq.js CHANGED
@@ -8,22 +8,22 @@ let model = null;
8
8
  const initGroq = () => {
9
9
  const apiKey = (0, config_1.getApiKey)();
10
10
  if (!apiKey) {
11
- throw new Error('API Key not found');
11
+ throw new Error("API Key not found");
12
12
  }
13
13
  model = new groq_1.ChatGroq({
14
14
  apiKey,
15
- model: 'llama-3.3-70b-versatile',
16
- temperature: 0.7
15
+ model: "llama-3.3-70b-versatile",
16
+ temperature: 0.7,
17
17
  });
18
18
  };
19
19
  exports.initGroq = initGroq;
20
20
  const streamChat = async (messages) => {
21
21
  if (!model)
22
22
  (0, exports.initGroq)();
23
- const langChainMessages = messages.map(m => {
24
- if (m.role === 'system')
23
+ const langChainMessages = messages.map((m) => {
24
+ if (m.role === "system")
25
25
  return new messages_1.SystemMessage(m.content);
26
- if (m.role === 'user')
26
+ if (m.role === "user")
27
27
  return new messages_1.HumanMessage(m.content);
28
28
  return new messages_1.AIMessage(m.content);
29
29
  });
@@ -33,23 +33,16 @@ class SafetyChecker {
33
33
  */
34
34
  isExtremelyDangerous(tool, params) {
35
35
  // Git operations that can't be easily undone
36
- if (tool.name === 'git_push' && params.force) {
36
+ if (tool.name === "git_push" && params.force) {
37
37
  return true;
38
38
  }
39
- if (tool.name === 'git_branch' && params.action === 'delete') {
39
+ if (tool.name === "git_branch" && params.action === "delete") {
40
40
  return true;
41
41
  }
42
42
  // Shell commands with dangerous patterns
43
- if (tool.name === 'bash') {
44
- const dangerous = [
45
- /rm\s+-rf/,
46
- /rm\s+.*\/\*/,
47
- />\s*\/dev/,
48
- /dd\s+if=/,
49
- /mkfs/,
50
- /format/i
51
- ];
52
- return dangerous.some(pattern => pattern.test(params.command));
43
+ if (tool.name === "bash") {
44
+ const dangerous = [/rm\s+-rf/, /rm\s+.*\/\*/, />\s*\/dev/, /dd\s+if=/, /mkfs/, /format/i];
45
+ return dangerous.some((pattern) => pattern.test(params.command));
53
46
  }
54
47
  return false;
55
48
  }
@@ -58,28 +51,28 @@ class SafetyChecker {
58
51
  */
59
52
  async promptConfirmation(tool, params) {
60
53
  const description = this.getOperationDescription(tool, params);
61
- console.log('\n⚠️ Confirmation required:');
54
+ console.log("\n⚠️ Confirmation required:");
62
55
  console.log(`Tool: ${tool.name}`);
63
56
  console.log(`Operation: ${description}`);
64
- console.log('');
57
+ console.log("");
65
58
  const { proceed, remember } = await inquirer_1.default.prompt([
66
59
  {
67
- type: 'confirm',
68
- name: 'proceed',
69
- message: 'Do you want to proceed with this operation?',
70
- default: false
60
+ type: "confirm",
61
+ name: "proceed",
62
+ message: "Do you want to proceed with this operation?",
63
+ default: false,
71
64
  },
72
65
  {
73
- type: 'confirm',
74
- name: 'remember',
75
- message: 'Remember this choice for similar operations in this session?',
66
+ type: "confirm",
67
+ name: "remember",
68
+ message: "Remember this choice for similar operations in this session?",
76
69
  default: false,
77
- when: (answers) => answers.proceed
78
- }
70
+ when: (answers) => answers.proceed,
71
+ },
79
72
  ]);
80
73
  return {
81
74
  confirmed: proceed,
82
- rememberChoice: remember || false
75
+ rememberChoice: remember || false,
83
76
  };
84
77
  }
85
78
  /**
@@ -87,20 +80,22 @@ class SafetyChecker {
87
80
  */
88
81
  getOperationDescription(tool, params) {
89
82
  switch (tool.name) {
90
- case 'write_file':
83
+ case "write_file":
91
84
  return `Write to file: ${params.file_path}`;
92
- case 'edit_file':
85
+ case "edit_file":
93
86
  return `Edit file: ${params.file_path} (replace "${params.search.substring(0, 50)}...")`;
94
- case 'bash':
87
+ case "bash":
95
88
  return `Execute command: ${params.command}`;
96
- case 'git_commit':
97
- const files = params.files ? ` (${params.files.length} files)` : '';
89
+ case "git_commit": {
90
+ const files = params.files ? ` (${params.files.length} files)` : "";
98
91
  return `Create git commit${files}: "${params.message}"`;
99
- case 'git_push':
100
- const force = params.force ? ' (FORCE)' : '';
101
- return `Push to remote${force}: ${params.branch || 'current branch'}`;
102
- case 'git_branch':
103
- return `${params.action} branch: ${params.name || 'N/A'}`;
92
+ }
93
+ case "git_push": {
94
+ const force = params.force ? " (FORCE)" : "";
95
+ return `Push to remote${force}: ${params.branch || "current branch"}`;
96
+ }
97
+ case "git_branch":
98
+ return `${params.action} branch: ${params.name || "N/A"}`;
104
99
  default:
105
100
  return JSON.stringify(params, null, 2);
106
101
  }