sam-coder-cli 2.0.4 → 2.0.7

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/bin/agi-cli.js CHANGED
@@ -9,12 +9,20 @@ const { exec } = require('child_process');
9
9
  const util = require('util');
10
10
  const execAsync = util.promisify(exec);
11
11
 
12
+ // Global error handlers to prevent silent crashes
13
+ process.on('unhandledRejection', (reason, promise) => {
14
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
15
+ // Don't exit, just log the error
16
+ });
17
+
18
+ process.on('uncaughtException', (error) => {
19
+ console.error('Uncaught Exception:', error);
20
+ // Don't exit, just log the error
21
+ });
22
+
12
23
  // Import AGI Animation module
13
24
  const { runAGIAnimation } = require('./agi-animation.js');
14
25
 
15
- // Import Brainstorm Core module
16
- const brainstormCore = require('./core');
17
-
18
26
  // Configuration
19
27
  const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
20
28
  let OPENROUTER_API_KEY;
@@ -72,17 +80,9 @@ const tools = [
72
80
  properties: {
73
81
  type: {
74
82
  type: 'string',
75
- enum: ['replace', 'insert', 'delete', 'search_replace'],
83
+ enum: ['replace', 'insert', 'delete', 'replace_block'],
76
84
  description: 'Type of edit operation'
77
85
  },
78
- old_string: {
79
- type: 'string',
80
- description: 'Exact string to search for and replace (for search_replace operations)'
81
- },
82
- new_string: {
83
- type: 'string',
84
- description: 'String to replace old_string with (for search_replace operations)'
85
- },
86
86
  startLine: {
87
87
  type: 'number',
88
88
  description: 'Starting line number (1-based)'
@@ -95,6 +95,14 @@ const tools = [
95
95
  type: 'string',
96
96
  description: 'New text to insert or replace with'
97
97
  },
98
+ oldText: {
99
+ type: 'string',
100
+ description: 'Exact text to search for and replace (for replace_block)'
101
+ },
102
+ replacementContent: {
103
+ type: 'string',
104
+ description: 'New text to replace oldText with (for replace_block)'
105
+ },
98
106
  pattern: {
99
107
  type: 'string',
100
108
  description: 'Pattern to search for (for replace operations)'
@@ -137,6 +145,14 @@ const tools = [
137
145
  },
138
146
  required: ['pattern', 'replacement']
139
147
  },
148
+ {
149
+ properties: {
150
+ type: { const: 'replace_block' },
151
+ oldText: { type: 'string' },
152
+ replacementContent: { type: 'string' }
153
+ },
154
+ required: ['oldText', 'replacementContent']
155
+ },
140
156
  {
141
157
  properties: {
142
158
  type: { const: 'insert' },
@@ -160,14 +176,6 @@ const tools = [
160
176
  endLine: { type: 'number' }
161
177
  },
162
178
  required: ['startLine', 'endLine']
163
- },
164
- {
165
- properties: {
166
- type: { const: 'search_replace' },
167
- old_string: { type: 'string' },
168
- new_string: { type: 'string' }
169
- },
170
- required: ['old_string', 'new_string']
171
179
  }
172
180
  ]
173
181
  }
@@ -233,48 +241,6 @@ INSTRUCTIONS:
233
241
 
234
242
  Always think step by step and explain your reasoning before taking actions that could affect the system.`;
235
243
 
236
- // System prompt for the AI Assistant when using Engineer Mode
237
- const ENGINEER_PROMPT = `You are a Senior Software Engineer with 15+ years of experience. You have deep expertise in:
238
- - Software architecture and design patterns
239
- - Clean code principles and best practices
240
- - Test-driven development
241
- - Performance optimization
242
- - Security best practices
243
- - Code review and mentoring
244
-
245
- TOOLS AVAILABLE:
246
- 1. readFile - Read the contents of a file
247
- 2. writeFile - Write content to a file
248
- 3. editFile - Edit specific parts of a file (use search_replace with old_string/new_string for precise edits)
249
- 4. runCommand - Execute a shell command
250
- 5. searchFiles - Search for files using a glob pattern
251
-
252
- ENGINEER PRINCIPLES:
253
- 1. **Code Quality First**: Write clean, maintainable, well-documented code
254
- 2. **Think Before Acting**: Analyze the problem thoroughly before making changes
255
- 3. **Small, Focused Changes**: Make incremental changes that are easy to review
256
- 4. **Test Your Work**: Verify changes work as expected before moving on
257
- 5. **Explain Your Reasoning**: Document why you made certain technical decisions
258
-
259
- WHEN EDITING FILES:
260
- - ALWAYS use editFile with search_replace operations
261
- - Use { "type": "search_replace", "old_string": "exact text to find", "new_string": "replacement text" }
262
- - The old_string must match EXACTLY including whitespace
263
- - Make focused, minimal changes
264
-
265
- WORKFLOW:
266
- 1. Read and understand the existing code
267
- 2. Identify the minimal changes needed
268
- 3. Make changes using precise search_replace operations
269
- 4. Verify the changes compile/run correctly
270
- 5. Summarize what was done
271
-
272
- ENVIRONMENT:
273
- - OS: ${process.platform}
274
- - Current directory: ${process.cwd()}
275
-
276
- You are autonomous - continue working until the task is complete. Use the 'stop' action when finished.`;
277
-
278
244
  // System prompt for the AI Assistant when using legacy function calling (JSON actions)
279
245
  const FUNCTION_CALLING_PROMPT = `You are an autonomous AI agent with advanced problem-solving capabilities. You operate through strategic action sequences to accomplish complex tasks on the user's system. Think like an expert developer and system administrator combined.
280
246
 
@@ -513,77 +479,94 @@ const agentUtils = {
513
479
  const edits = typeof inputPathOrObj === 'string' ? maybeEdits : inputPathOrObj?.edits;
514
480
  if (!targetPath) throw new Error('editFile: missing path');
515
481
  if (!edits) throw new Error('editFile: missing edits');
482
+
516
483
  // Read the current file content
517
484
  let content = await fs.readFile(targetPath, 'utf-8');
518
- const lines = content.split('\n');
485
+ let lines = content.split('\n');
519
486
 
520
487
  // Process each edit operation
521
- for (const op of edits.operations) {
522
- switch (op.type) {
523
- case 'replace':
524
- if (op.startLine !== undefined && op.endLine !== undefined) {
525
- // Line-based replacement
526
- if (op.startLine < 1 || op.endLine > lines.length) {
527
- throw new Error(`Line numbers out of range (1-${lines.length})`);
488
+ for (let i = 0; i < edits.operations.length; i++) {
489
+ const op = edits.operations[i];
490
+ try {
491
+ switch (op.type) {
492
+ case 'replace':
493
+ if (op.startLine !== undefined && op.endLine !== undefined) {
494
+ // Line-based replacement
495
+ if (op.startLine < 1 || op.endLine > lines.length) {
496
+ throw new Error(`Line numbers out of range (1-${lines.length})`);
497
+ }
498
+ if (typeof op.newText !== 'string') {
499
+ throw new Error(`'newText' must be a string for replace operation`);
500
+ }
501
+ const before = lines.slice(0, op.startLine - 1);
502
+ const after = lines.slice(op.endLine);
503
+ const newLines = op.newText.split('\n');
504
+ lines = [...before, ...newLines, ...after];
505
+ } else if (op.pattern) {
506
+ // Pattern-based replacement
507
+ if (typeof op.replacement !== 'string') {
508
+ throw new Error(`'replacement' must be a string for pattern-based replace`);
509
+ }
510
+ const regex = new RegExp(op.pattern, op.flags || '');
511
+ content = lines.join('\n');
512
+ content = content.replace(regex, op.replacement);
513
+ lines = content.split('\n');
514
+ } else {
515
+ throw new Error('Replace operation requires either startLine/endLine/newText or pattern/replacement');
528
516
  }
529
- const before = lines.slice(0, op.startLine - 1);
530
- const after = lines.slice(op.endLine);
531
- const newLines = op.newText.split('\n');
532
- lines.splice(0, lines.length, ...before, ...newLines, ...after);
533
- } else if (op.pattern) {
534
- // Pattern-based replacement
535
- const regex = new RegExp(op.pattern, op.flags || '');
536
- content = content.replace(regex, op.replacement);
537
- // Update lines array for subsequent operations
538
- lines.length = 0;
539
- lines.push(...content.split('\n'));
540
- }
541
- break;
542
-
543
- case 'insert':
544
- if (op.position === 'start') {
545
- lines.unshift(...op.text.split('\n'));
546
- } else if (op.position === 'end') {
547
- lines.push(...op.text.split('\n'));
548
- } else if (op.line !== undefined) {
549
- if (op.line < 1 || op.line > lines.length + 1) {
550
- throw new Error(`Line number out of range (1-${lines.length + 1})`);
517
+ break;
518
+
519
+ case 'replace_block':
520
+ if (typeof op.oldText !== 'string' || typeof op.replacementContent !== 'string') {
521
+ throw new Error('replace_block requires both oldText and replacementContent as strings');
551
522
  }
552
- const insertLines = op.text.split('\n');
553
- lines.splice(op.line - 1, 0, ...insertLines);
554
- }
555
- break;
523
+ content = lines.join('\n');
524
+ if (!content.includes(op.oldText)) {
525
+ throw new Error('Could not find exact match for oldText in file');
526
+ }
527
+ // Check for uniqueness
528
+ const occurrences = content.split(op.oldText).length - 1;
529
+ if (occurrences > 1) {
530
+ throw new Error(`Found ${occurrences} occurrences of oldText. Please be more specific to ensure a unique match.`);
531
+ }
532
+ content = content.replace(op.oldText, op.replacementContent);
533
+ lines = content.split('\n');
534
+ break;
556
535
 
557
- case 'delete':
558
- if (op.startLine < 1 || op.endLine > lines.length) {
559
- throw new Error(`Line numbers out of range (1-${lines.length})`);
560
- }
561
- lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
562
- break;
536
+ case 'insert':
537
+ if (typeof op.text !== 'string') {
538
+ throw new Error(`'text' must be a string for insert operation`);
539
+ }
540
+ if (op.position === 'start') {
541
+ lines.unshift(...op.text.split('\n'));
542
+ } else if (op.position === 'end') {
543
+ lines.push(...op.text.split('\n'));
544
+ } else if (op.line !== undefined) {
545
+ if (op.line < 1 || op.line > lines.length + 1) {
546
+ throw new Error(`Line number out of range (1-${lines.length + 1})`);
547
+ }
548
+ const insertLines = op.text.split('\n');
549
+ lines.splice(op.line - 1, 0, ...insertLines);
550
+ } else {
551
+ throw new Error('Insert operation requires either position (start/end) or line number');
552
+ }
553
+ break;
563
554
 
564
- case 'search_replace':
565
- // Claude Code style: exact string search and replace
566
- if (op.old_string === undefined || op.new_string === undefined) {
567
- throw new Error('search_replace requires old_string and new_string');
568
- }
569
- content = lines.join('\n');
570
- if (!content.includes(op.old_string)) {
571
- throw new Error(`Could not find exact match for old_string: "${op.old_string.substring(0, 50)}${op.old_string.length > 50 ? '...' : ''}"`);
572
- }
573
- // Count occurrences
574
- const occurrences = content.split(op.old_string).length - 1;
575
- if (occurrences > 1) {
576
- console.log(`Warning: Found ${occurrences} occurrences of old_string. Replacing first occurrence only.`);
577
- }
578
- // Replace first occurrence (to be safe and predictable like Claude Code)
579
- content = content.replace(op.old_string, op.new_string);
580
- // Update lines array
581
- lines.length = 0;
582
- lines.push(...content.split('\n'));
583
- break;
584
-
585
- default:
586
- throw new Error(`Unknown operation type: ${op.type}`);
555
+ case 'delete':
556
+ if (op.startLine === undefined || op.endLine === undefined) {
557
+ throw new Error('Delete operation requires both startLine and endLine');
558
+ }
559
+ if (op.startLine < 1 || op.endLine > lines.length) {
560
+ throw new Error(`Line numbers out of range (1-${lines.length})`);
561
+ }
562
+ lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
563
+ break;
564
+
565
+ default:
566
+ throw new Error(`Unknown operation type: ${op.type}`);
567
+ }
568
+ } catch (opError) {
569
+ throw new Error(`Operation #${i + 1} (${op.type}) failed: ${opError.message}`);
587
570
  }
588
571
  }
589
572
 
@@ -592,7 +575,8 @@ const agentUtils = {
592
575
  return `Successfully edited ${targetPath}`;
593
576
 
594
577
  } catch (error) {
595
- throw new Error(`Failed to edit file ${typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path}: ${error.message}`);
578
+ const pathInfo = typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path;
579
+ throw new Error(`Failed to edit file ${pathInfo}: ${error.message}`);
596
580
  }
597
581
  },
598
582
 
@@ -1455,225 +1439,145 @@ async function processQuery(query, conversation = [], currentModel) {
1455
1439
  }
1456
1440
  }
1457
1441
 
1458
- async function chat(rl, mode, initialModel) {
1459
- let currentModel = initialModel;
1460
- const conversation = [];
1461
-
1462
- // Initialize conversation with appropriate system prompt based on mode
1463
- if (mode === 'engineer') {
1464
- conversation.push({ role: 'system', content: ENGINEER_PROMPT });
1465
- } else if (mode === 'tool') {
1466
- conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1467
- } else {
1468
- conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1469
- }
1470
-
1471
- // Determine if we should use tool calling API (both 'tool' and 'engineer' modes use tools)
1472
- const useToolCalling = (mode === 'tool' || mode === 'engineer');
1473
-
1474
- console.log('Type your message, or "exit" to quit.');
1475
-
1476
- // Use async iterator pattern instead of event-based 'on' to avoid async callback issues
1477
- while (true) {
1478
- const input = await new Promise((resolve) => {
1479
- rl.question('> ', resolve);
1480
- });
1481
-
1482
- // Handle commands
1483
- if (input.toLowerCase().startsWith('/model')) {
1484
- const newModel = input.split(' ')[1];
1485
- if (newModel) {
1486
- currentModel = newModel;
1487
- let config = await readConfig() || {};
1488
- config.MODEL = currentModel;
1489
- await writeConfig(config);
1490
- console.log(`Model changed to: ${currentModel}`);
1491
- } else {
1492
- console.log('Please specify a model. Usage: /model <model_name>');
1493
- }
1494
- continue;
1495
- }
1496
-
1497
- if (input.toLowerCase().startsWith('/thoughts')) {
1498
- const parts = input.trim().split(/\s+/);
1499
- const arg = parts[1] ? parts[1].toLowerCase() : '';
1500
- if (arg !== 'on' && arg !== 'off') {
1501
- const state = SHOW_THOUGHTS ? 'on' : 'off';
1502
- ui.showInfo(`Usage: /thoughts on|off (currently ${state})`);
1503
- continue;
1504
- }
1505
- const enable = arg === 'on';
1506
- SHOW_THOUGHTS = enable;
1507
- let config = await readConfig() || {};
1508
- config.showThoughts = enable;
1509
- await writeConfig(config);
1510
- ui.showResponse(`Hidden thoughts ${enable ? 'enabled' : 'disabled'}.`);
1511
- continue;
1512
- }
1513
-
1514
- if (input.toLowerCase() === '/default-model') {
1515
- currentModel = 'deepseek/deepseek-chat-v3-0324:free';
1516
- let config = await readConfig() || {};
1517
- config.MODEL = currentModel;
1518
- await writeConfig(config);
1519
- console.log(`Model reset to default: ${currentModel}`);
1520
- continue;
1521
- }
1442
+ async function chat(rl, useToolCalling, initialModel) {
1443
+ return new Promise((resolve) => {
1444
+ let currentModel = initialModel;
1445
+ const conversation = [];
1522
1446
 
1523
- if (input.toLowerCase() === '/setup') {
1524
- await runSetup(rl, true);
1525
- console.log('\nSetup complete. Please restart the application to apply changes.');
1526
- rl.close();
1527
- return;
1447
+ // Initialize conversation with appropriate system prompt
1448
+ if (useToolCalling) {
1449
+ conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1450
+ } else {
1451
+ conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1528
1452
  }
1529
1453
 
1530
- // Brainstorm command: /brainstorm <project-name> <description>
1531
- if (input.toLowerCase().startsWith('/brainstorm')) {
1532
- const parts = input.substring('/brainstorm'.length).trim();
1533
- if (!parts) {
1534
- console.log('Usage: /brainstorm <project-name> "<description>"');
1535
- console.log('Example: /brainstorm "My Project" "A cool project that does things"');
1536
- continue;
1537
- }
1538
-
1539
- // Parse project name and description
1540
- const match = parts.match(/^"?([^"]+)"?\s+"?([^"]+)"?$/) || parts.match(/^(\S+)\s+(.+)$/);
1541
- if (!match) {
1542
- console.log('Usage: /brainstorm <project-name> "<description>"');
1543
- continue;
1544
- }
1454
+ console.log('Type your message, or "exit" to quit.');
1545
1455
 
1546
- const projectName = match[1].trim();
1547
- const projectDescription = match[2].trim();
1548
- const outputDir = path.join(process.cwd(), projectName.replace(/\s+/g, '-').toLowerCase());
1456
+ rl.setPrompt('> ');
1457
+ rl.prompt();
1549
1458
 
1459
+ rl.on('line', async (input) => {
1550
1460
  try {
1551
- ui.showInfo(`Creating brainstorm session for "${projectName}"...`);
1552
- const session = await brainstormCore.quickStart(
1553
- projectName,
1554
- projectDescription,
1555
- outputDir,
1556
- ['CLAUDE-1'] // Single agent by default
1557
- );
1558
-
1559
- ui.showSuccess(`✅ Brainstorm session created!`);
1560
- console.log(` Session ID: ${session.id}`);
1561
- console.log(` Output: ${outputDir}`);
1562
- console.log(` Files generated: ${Object.keys(session.fileVersions).length}`);
1563
- console.log(`\n Generated files:`);
1564
- Object.keys(session.fileVersions).forEach(f => console.log(` - ${f}`));
1565
- console.log(`\n Use "/finish <summary>" when done to complete the session.`);
1566
-
1567
- // Store current session path for /finish command
1568
- global.currentSessionDir = outputDir;
1569
- } catch (error) {
1570
- ui.showError(`Failed to create brainstorm: ${error.message}`);
1571
- }
1572
- continue;
1573
- }
1574
-
1575
- // Finish command: /finish <summary>
1576
- if (input.toLowerCase().startsWith('/finish')) {
1577
- const summary = input.substring('/finish'.length).trim();
1461
+ // Handle empty input - just reprompt
1462
+ if (!input || !input.trim()) {
1463
+ rl.prompt();
1464
+ return;
1465
+ }
1578
1466
 
1579
- if (!global.currentSessionDir) {
1580
- console.log('No active brainstorm session. Use /brainstorm first.');
1581
- continue;
1582
- }
1467
+ if (input.toLowerCase().startsWith('/model')) {
1468
+ const newModel = input.split(' ')[1];
1469
+ if (newModel) {
1470
+ currentModel = newModel;
1471
+ let config = await readConfig() || {};
1472
+ config.MODEL = currentModel;
1473
+ await writeConfig(config);
1474
+ console.log(`Model changed to: ${currentModel}`);
1475
+ } else {
1476
+ console.log('Please specify a model. Usage: /model <model_name>');
1477
+ }
1478
+ rl.prompt();
1479
+ return;
1480
+ }
1583
1481
 
1584
- if (!summary) {
1585
- console.log('Usage: /finish "<summary of what was accomplished>"');
1586
- continue;
1587
- }
1482
+ if (input.toLowerCase().startsWith('/thoughts')) {
1483
+ const parts = input.trim().split(/\s+/);
1484
+ const arg = parts[1] ? parts[1].toLowerCase() : '';
1485
+ if (arg !== 'on' && arg !== 'off') {
1486
+ const state = SHOW_THOUGHTS ? 'on' : 'off';
1487
+ ui.showInfo(`Usage: /thoughts on|off (currently ${state})`);
1488
+ rl.prompt();
1489
+ return;
1490
+ }
1491
+ const enable = arg === 'on';
1492
+ SHOW_THOUGHTS = enable;
1493
+ let config = await readConfig() || {};
1494
+ config.showThoughts = enable;
1495
+ await writeConfig(config);
1496
+ ui.showResponse(`Hidden thoughts ${enable ? 'enabled' : 'disabled'}.`);
1497
+ rl.prompt();
1498
+ return;
1499
+ }
1588
1500
 
1589
- try {
1590
- ui.showInfo('Completing brainstorm session...');
1591
- const result = await brainstormCore.finishBrainstorm({
1592
- sessionDir: global.currentSessionDir,
1593
- summary,
1594
- actor: 'CLAUDE-1'
1595
- });
1501
+ if (input.toLowerCase() === '/default-model') {
1502
+ currentModel = 'deepseek/deepseek-chat-v3-0324:free';
1503
+ let config = await readConfig() || {};
1504
+ config.MODEL = currentModel;
1505
+ await writeConfig(config);
1506
+ console.log(`Model reset to default: ${currentModel}`);
1507
+ rl.prompt();
1508
+ return;
1509
+ }
1596
1510
 
1597
- if (result.success) {
1598
- ui.showSuccess(`✅ Session completed!`);
1599
- console.log(` Summary: ${summary}`);
1600
- global.currentSessionDir = null;
1601
- } else {
1602
- ui.showError(`Failed: ${result.errors.join(', ')}`);
1511
+ if (input.toLowerCase() === '/setup') {
1512
+ await runSetup(rl, true);
1513
+ console.log('\nSetup complete. Please restart the application to apply changes.');
1514
+ rl.close();
1515
+ return;
1603
1516
  }
1604
- } catch (error) {
1605
- ui.showError(`Failed to finish session: ${error.message}`);
1606
- }
1607
- continue;
1608
- }
1609
1517
 
1610
- if (input.toLowerCase() === 'exit') {
1611
- ui.showResponse('Goodbye!');
1612
- rl.close();
1613
- return;
1614
- }
1518
+ if (input.toLowerCase() === 'exit') {
1519
+ rl.close();
1520
+ return;
1521
+ }
1615
1522
 
1616
- // Direct Harmony tool-call execution from user input (bypass model)
1617
- try {
1618
- const seg = parseSegmentedTranscript(input);
1619
- if (seg && seg.segmented && seg.recoveredToolCalls && seg.recoveredToolCalls.length) {
1620
- const messages = [];
1621
- if (useToolCalling) {
1622
- messages.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1623
- } else {
1624
- messages.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1523
+ // Direct Harmony tool-call execution from user input (bypass model)
1524
+ try {
1525
+ const seg = parseSegmentedTranscript(input);
1526
+ if (seg && seg.segmented && seg.recoveredToolCalls && seg.recoveredToolCalls.length) {
1527
+ const messages = [];
1528
+ if (useToolCalling) {
1529
+ messages.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1530
+ } else {
1531
+ messages.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1532
+ }
1533
+ messages.push({ role: 'user', content: input });
1534
+ ui.startThinking();
1535
+ const results = await handleToolCalls(seg.recoveredToolCalls, messages);
1536
+ ui.stopThinking();
1537
+ if (results.length === 1) {
1538
+ ui.showSuccess(`Tool '${results[0].name}' executed.`);
1539
+ } else {
1540
+ ui.showSuccess(`Executed ${results.length} tool calls.`);
1541
+ }
1542
+ // Show concise outputs
1543
+ results.forEach(r => {
1544
+ const preview = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
1545
+ ui.showInfo(`${r.name}: ${preview.length > 300 ? preview.slice(0, 300) + '...' : preview}`);
1546
+ });
1547
+ rl.prompt();
1548
+ return;
1549
+ }
1550
+ } catch (e) {
1551
+ // Fall through to normal processing if parsing/execution fails
1625
1552
  }
1626
- messages.push({ role: 'user', content: input });
1627
- ui.startThinking();
1628
- const results = await handleToolCalls(seg.recoveredToolCalls, messages);
1553
+
1554
+ const result = useToolCalling
1555
+ ? await processQueryWithTools(input, conversation, currentModel)
1556
+ : await processQuery(input, conversation, currentModel);
1629
1557
  ui.stopThinking();
1630
- if (results.length === 1) {
1631
- ui.showSuccess(`Tool '${results[0].name}' executed.`);
1632
- } else {
1633
- ui.showSuccess(`Executed ${results.length} tool calls.`);
1634
- }
1635
- // Show concise outputs
1636
- results.forEach(r => {
1637
- const preview = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
1638
- ui.showInfo(`${r.name}: ${preview.length > 300 ? preview.slice(0, 300) + '...' : preview}`);
1639
- });
1640
- continue;
1641
- }
1642
- } catch (e) {
1643
- // Fall through to normal processing if parsing/execution fails
1644
- }
1558
+ ui.showResponse(result.response);
1645
1559
 
1646
- // Main query processing
1647
- try {
1648
- const result = useToolCalling
1649
- ? await processQueryWithTools(input, conversation, currentModel)
1650
- : await processQuery(input, conversation, currentModel);
1651
- ui.stopThinking();
1652
- ui.showResponse(result.response);
1560
+ conversation.length = 0;
1561
+ result.conversation.forEach(msg => conversation.push(msg));
1653
1562
 
1654
- conversation.length = 0;
1655
- result.conversation.forEach(msg => conversation.push(msg));
1656
- } catch (error) {
1657
- ui.stopThinking();
1658
- console.error('❌ Error processing request:', error.message);
1659
- if (process.env.DEBUG) {
1660
- console.error(error.stack);
1563
+ rl.prompt();
1564
+ } catch (error) {
1565
+ ui.stopThinking();
1566
+ ui.showError(`Unexpected error: ${error.message}`);
1567
+ console.error(error);
1568
+ rl.prompt();
1661
1569
  }
1662
- }
1663
- }
1570
+ }).on('close', () => {
1571
+ ui.showResponse('Goodbye!');
1572
+ resolve();
1573
+ });
1574
+ });
1664
1575
  }
1665
1576
 
1666
1577
  function askForMode(rl) {
1667
1578
  return new Promise((resolve) => {
1668
- rl.question('Select mode (1 for tool calling, 2 for function calling, 3 for engineer): ', (answer) => {
1669
- const mode = answer.trim();
1670
- if (mode === '3') {
1671
- resolve('engineer');
1672
- } else if (mode === '2') {
1673
- resolve('function');
1674
- } else {
1675
- resolve('tool');
1676
- }
1579
+ rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
1580
+ resolve(answer.trim() === '1');
1677
1581
  });
1678
1582
  });
1679
1583
  }
@@ -1769,14 +1673,11 @@ async function start() {
1769
1673
  console.log('Select Mode:');
1770
1674
  console.log('1. Tool Calling (for models that support it)');
1771
1675
  console.log('2. Function Calling (legacy)');
1772
- console.log('3. Engineer Mode (autonomous engineering with Claude Code style editing)');
1773
1676
 
1774
- const selectedMode = await askForMode(rl);
1775
- const modeNames = { 'tool': 'Tool Calling', 'function': 'Function Calling', 'engineer': 'Engineer' };
1776
- ui.showResponse(`\nStarting in ${modeNames[selectedMode]} mode...\n`);
1677
+ const useToolCalling = await askForMode(rl);
1678
+ ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
1777
1679
 
1778
- await chat(rl, selectedMode, MODEL);
1779
- process.exit(0);
1680
+ await chat(rl, useToolCalling, MODEL);
1780
1681
  } catch (error) {
1781
1682
  ui.showError(error);
1782
1683
  rl.close();
@@ -1784,12 +1685,4 @@ async function start() {
1784
1685
  }
1785
1686
  }
1786
1687
 
1787
- // Keep the process alive and handle uncaught errors
1788
- process.on('uncaughtException', (err) => {
1789
- console.error('Uncaught Exception:', err.message);
1790
- });
1791
- process.on('unhandledRejection', (reason) => {
1792
- console.error('Unhandled Rejection:', reason);
1793
- });
1794
-
1795
1688
  start().catch(console.error);