sam-coder-cli 1.0.69 → 2.0.1

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
@@ -12,6 +12,9 @@ const execAsync = util.promisify(exec);
12
12
  // Import AGI Animation module
13
13
  const { runAGIAnimation } = require('./agi-animation.js');
14
14
 
15
+ // Import Brainstorm Core module
16
+ const brainstormCore = require('./core');
17
+
15
18
  // Configuration
16
19
  const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
17
20
  let OPENROUTER_API_KEY;
@@ -67,43 +70,51 @@ const tools = [
67
70
  items: {
68
71
  type: 'object',
69
72
  properties: {
70
- type: {
71
- type: 'string',
72
- enum: ['replace', 'insert', 'delete'],
73
+ type: {
74
+ type: 'string',
75
+ enum: ['replace', 'insert', 'delete', 'search_replace'],
73
76
  description: 'Type of edit operation'
74
77
  },
75
- startLine: {
76
- type: 'number',
77
- description: 'Starting line number (1-based)'
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)'
78
85
  },
79
- endLine: {
80
- type: 'number',
81
- description: 'Ending line number (1-based, inclusive)'
86
+ startLine: {
87
+ type: 'number',
88
+ description: 'Starting line number (1-based)'
82
89
  },
83
- newText: {
84
- type: 'string',
85
- description: 'New text to insert or replace with'
90
+ endLine: {
91
+ type: 'number',
92
+ description: 'Ending line number (1-based, inclusive)'
86
93
  },
87
- pattern: {
88
- type: 'string',
89
- description: 'Pattern to search for (for replace operations)'
94
+ newText: {
95
+ type: 'string',
96
+ description: 'New text to insert or replace with'
90
97
  },
91
- replacement: {
92
- type: 'string',
93
- description: 'Replacement text (for pattern-based replace)'
98
+ pattern: {
99
+ type: 'string',
100
+ description: 'Pattern to search for (for replace operations)'
94
101
  },
95
- flags: {
96
- type: 'string',
97
- description: 'Regex flags (e.g., "g" for global)'
102
+ replacement: {
103
+ type: 'string',
104
+ description: 'Replacement text (for pattern-based replace)'
98
105
  },
99
- position: {
100
- type: 'string',
106
+ flags: {
107
+ type: 'string',
108
+ description: 'Regex flags (e.g., "g" for global)'
109
+ },
110
+ position: {
111
+ type: 'string',
101
112
  enum: ['start', 'end'],
102
- description: 'Where to insert (only for insert operations)'
113
+ description: 'Where to insert (only for insert operations)'
103
114
  },
104
- line: {
105
- type: 'number',
106
- description: 'Line number to insert at (for line-based insert)'
115
+ line: {
116
+ type: 'number',
117
+ description: 'Line number to insert at (for line-based insert)'
107
118
  }
108
119
  },
109
120
  required: ['type'],
@@ -149,6 +160,14 @@ const tools = [
149
160
  endLine: { type: 'number' }
150
161
  },
151
162
  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']
152
171
  }
153
172
  ]
154
173
  }
@@ -214,6 +233,48 @@ INSTRUCTIONS:
214
233
 
215
234
  Always think step by step and explain your reasoning before taking actions that could affect the system.`;
216
235
 
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
+
217
278
  // System prompt for the AI Assistant when using legacy function calling (JSON actions)
218
279
  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.
219
280
 
@@ -455,7 +516,7 @@ const agentUtils = {
455
516
  // Read the current file content
456
517
  let content = await fs.readFile(targetPath, 'utf-8');
457
518
  const lines = content.split('\n');
458
-
519
+
459
520
  // Process each edit operation
460
521
  for (const op of edits.operations) {
461
522
  switch (op.type) {
@@ -478,7 +539,7 @@ const agentUtils = {
478
539
  lines.push(...content.split('\n'));
479
540
  }
480
541
  break;
481
-
542
+
482
543
  case 'insert':
483
544
  if (op.position === 'start') {
484
545
  lines.unshift(...op.text.split('\n'));
@@ -492,23 +553,44 @@ const agentUtils = {
492
553
  lines.splice(op.line - 1, 0, ...insertLines);
493
554
  }
494
555
  break;
495
-
556
+
496
557
  case 'delete':
497
558
  if (op.startLine < 1 || op.endLine > lines.length) {
498
559
  throw new Error(`Line numbers out of range (1-${lines.length})`);
499
560
  }
500
561
  lines.splice(op.startLine - 1, op.endLine - op.startLine + 1);
501
562
  break;
502
-
563
+
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
+
503
585
  default:
504
586
  throw new Error(`Unknown operation type: ${op.type}`);
505
587
  }
506
588
  }
507
-
589
+
508
590
  // Write the modified content back to the file
509
591
  await fs.writeFile(targetPath, lines.join('\n'), 'utf-8');
510
592
  return `Successfully edited ${targetPath}`;
511
-
593
+
512
594
  } catch (error) {
513
595
  throw new Error(`Failed to edit file ${typeof inputPathOrObj === 'string' ? inputPathOrObj : inputPathOrObj?.path}: ${error.message}`);
514
596
  }
@@ -661,7 +743,7 @@ function extractJsonFromMarkdown(text) {
661
743
  // Try to find a markdown code block with JSON content (case insensitive)
662
744
  const codeBlockRegex = /```(?:json|JSON)\s*([\s\S]*?)\s*```/i;
663
745
  const match = text.match(codeBlockRegex);
664
-
746
+
665
747
  if (match && match[1]) {
666
748
  try {
667
749
  const jsonStr = match[1].trim();
@@ -673,7 +755,7 @@ function extractJsonFromMarkdown(text) {
673
755
  // ignore
674
756
  }
675
757
  }
676
-
758
+
677
759
  // If no code block found, look for JSON-like patterns in the text
678
760
  const jsonPatterns = [
679
761
  // Look for objects that start with { and end with }
@@ -681,7 +763,7 @@ function extractJsonFromMarkdown(text) {
681
763
  // Look for arrays that start with [ and end with ]
682
764
  /\[[\s\S]*?\]/g
683
765
  ];
684
-
766
+
685
767
  for (const pattern of jsonPatterns) {
686
768
  const matches = text.match(pattern);
687
769
  if (matches) {
@@ -699,7 +781,7 @@ function extractJsonFromMarkdown(text) {
699
781
  }
700
782
  }
701
783
  }
702
-
784
+
703
785
  // If still no valid JSON found, try to parse the entire text as JSON
704
786
  try {
705
787
  const trimmed = text.trim();
@@ -709,7 +791,7 @@ function extractJsonFromMarkdown(text) {
709
791
  } catch (error) {
710
792
  // Last resort failed
711
793
  }
712
-
794
+
713
795
  return null;
714
796
  }
715
797
 
@@ -975,24 +1057,24 @@ function extractArgsJson(text) {
975
1057
  // Prefer fenced code block
976
1058
  const fence = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
977
1059
  if (fence && fence[1]) {
978
- try { return JSON.parse(fence[1].trim()); } catch (_) {}
1060
+ try { return JSON.parse(fence[1].trim()); } catch (_) { }
979
1061
  }
980
1062
  // Try straightforward parse
981
1063
  const trimmed = text.trim();
982
1064
  if ((trimmed.startsWith('{') && trimmed.includes('}')) || (trimmed.startsWith('[') && trimmed.includes(']'))) {
983
- try { return JSON.parse(trimmed); } catch (_) {}
1065
+ try { return JSON.parse(trimmed); } catch (_) { }
984
1066
  }
985
1067
  // Fallback: find first {...} minimally
986
1068
  const braceRegex = /\{[\s\S]*?\}/g;
987
1069
  const m = braceRegex.exec(text);
988
1070
  if (m && m[0]) {
989
- try { return JSON.parse(m[0]); } catch (_) {}
1071
+ try { return JSON.parse(m[0]); } catch (_) { }
990
1072
  }
991
1073
  // Fallback: find first [...] minimally
992
1074
  const arrRegex = /\[[\s\S]*?\]/g;
993
1075
  const a = arrRegex.exec(text);
994
1076
  if (a && a[0]) {
995
- try { return JSON.parse(a[0]); } catch (_) {}
1077
+ try { return JSON.parse(a[0]); } catch (_) { }
996
1078
  }
997
1079
  return {};
998
1080
  }
@@ -1162,11 +1244,11 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
1162
1244
 
1163
1245
  async function handleToolCalls(toolCalls, messages) {
1164
1246
  const results = [];
1165
-
1247
+
1166
1248
  for (const toolCall of toolCalls) {
1167
1249
  const functionName = toolCall.function.name;
1168
1250
  let args;
1169
-
1251
+
1170
1252
  try {
1171
1253
  args = JSON.parse(toolCall.function.arguments);
1172
1254
  } catch (error) {
@@ -1179,14 +1261,14 @@ async function handleToolCalls(toolCalls, messages) {
1179
1261
  });
1180
1262
  continue;
1181
1263
  }
1182
-
1264
+
1183
1265
  console.log(`🔧 Executing ${functionName} with args:`, args);
1184
-
1266
+
1185
1267
  try {
1186
1268
  if (!agentUtils[functionName]) {
1187
1269
  throw new Error(`Tool '${functionName}' not found`);
1188
1270
  }
1189
-
1271
+
1190
1272
  let result;
1191
1273
  if (Array.isArray(args)) {
1192
1274
  result = await agentUtils[functionName](...args);
@@ -1198,9 +1280,9 @@ async function handleToolCalls(toolCalls, messages) {
1198
1280
  result = await agentUtils[functionName]();
1199
1281
  }
1200
1282
  console.log('✅ Tool executed successfully');
1201
-
1283
+
1202
1284
  const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
1203
-
1285
+
1204
1286
  results.push({
1205
1287
  tool_call_id: toolCall.id,
1206
1288
  role: 'tool',
@@ -1209,25 +1291,25 @@ async function handleToolCalls(toolCalls, messages) {
1209
1291
  });
1210
1292
  } catch (error) {
1211
1293
  console.error('❌ Tool execution failed:', error);
1212
-
1294
+
1213
1295
  results.push({
1214
1296
  tool_call_id: toolCall.id,
1215
1297
  role: 'tool',
1216
1298
  name: functionName,
1217
- content: JSON.stringify({
1299
+ content: JSON.stringify({
1218
1300
  error: error.message,
1219
1301
  stack: process.env.DEBUG ? error.stack : undefined
1220
1302
  })
1221
1303
  });
1222
1304
  }
1223
1305
  }
1224
-
1306
+
1225
1307
  return results;
1226
1308
  }
1227
1309
 
1228
1310
  async function executeAction(action) {
1229
1311
  const { type, data } = action;
1230
-
1312
+
1231
1313
  switch (type) {
1232
1314
  case 'read':
1233
1315
  return await agentUtils.readFile(data.path);
@@ -1266,16 +1348,16 @@ async function executeAction(action) {
1266
1348
  async function processQuery(query, conversation = [], currentModel) {
1267
1349
  try {
1268
1350
  const userMessage = { role: 'user', content: query };
1269
-
1351
+
1270
1352
  // Use existing conversation (which should already have system prompt from chat function)
1271
1353
  let messages = [...conversation, userMessage];
1272
-
1354
+
1273
1355
  let actionCount = 0;
1274
1356
  const MAX_ACTIONS = 10;
1275
1357
  let finalResponse = '';
1276
1358
 
1277
1359
  ui.startThinking();
1278
-
1360
+
1279
1361
  while (actionCount < MAX_ACTIONS) {
1280
1362
  const responseObj = await callOpenRouter(messages, currentModel, true);
1281
1363
  const assistantMessage = responseObj.choices[0].message;
@@ -1295,55 +1377,55 @@ async function processQuery(query, conversation = [], currentModel) {
1295
1377
  }
1296
1378
  normalizeToolCallsFromMessage(assistantMessage);
1297
1379
  messages.push(assistantMessage);
1298
-
1380
+
1299
1381
  const actionData = extractJsonFromMarkdown(assistantMessage.content);
1300
-
1382
+
1301
1383
  // If no valid action found, treat as final response
1302
1384
  if (!actionData || !actionData.type) {
1303
1385
  finalResponse = assistantMessage.content;
1304
1386
  break;
1305
1387
  }
1306
-
1388
+
1307
1389
  // If stop action, break with the reasoning or content
1308
1390
  if (actionData.type === 'stop') {
1309
1391
  finalResponse = actionData.reasoning || assistantMessage.content;
1310
1392
  break;
1311
1393
  }
1312
-
1394
+
1313
1395
  actionCount++;
1314
1396
  console.log(`🔧 Executing action ${actionCount}: ${actionData.type}`);
1315
1397
  if (actionData.reasoning) {
1316
1398
  console.log(`📝 Reasoning: ${actionData.reasoning}`);
1317
1399
  }
1318
-
1400
+
1319
1401
  try {
1320
1402
  const result = await executeAction(actionData);
1321
1403
  console.log('✅ Action executed successfully');
1322
-
1404
+
1323
1405
  // Add action result to conversation
1324
1406
  const resultMessage = {
1325
- role: 'user',
1407
+ role: 'user',
1326
1408
  content: `Action result (${actionData.type}): ${result}`
1327
1409
  };
1328
1410
  messages.push(resultMessage);
1329
-
1411
+
1330
1412
  } catch (error) {
1331
1413
  console.error('❌ Action execution failed:', error.message);
1332
-
1414
+
1333
1415
  // Add error result to conversation
1334
1416
  const errorMessage = {
1335
- role: 'user',
1417
+ role: 'user',
1336
1418
  content: `Action failed (${actionData.type}): ${error.message}`
1337
1419
  };
1338
1420
  messages.push(errorMessage);
1339
1421
  }
1340
1422
  }
1341
-
1423
+
1342
1424
  // If we hit max actions, get a final response
1343
1425
  if (actionCount >= MAX_ACTIONS && !finalResponse) {
1344
1426
  const finalMsg = { role: 'user', content: 'Please provide a final summary of what was accomplished.' };
1345
1427
  messages.push(finalMsg);
1346
-
1428
+
1347
1429
  const finalResponseObj = await callOpenRouter(messages, currentModel, true);
1348
1430
  const finalAssistantMessage = finalResponseObj.choices[0].message;
1349
1431
  if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
@@ -1373,22 +1455,27 @@ async function processQuery(query, conversation = [], currentModel) {
1373
1455
  }
1374
1456
  }
1375
1457
 
1376
- async function chat(rl, useToolCalling, initialModel) {
1458
+ async function chat(rl, mode, initialModel) {
1377
1459
  let currentModel = initialModel;
1378
1460
  const conversation = [];
1379
-
1380
- // Initialize conversation with appropriate system prompt
1381
- if (useToolCalling) {
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') {
1382
1466
  conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1383
1467
  } else {
1384
1468
  conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1385
1469
  }
1386
-
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
+
1387
1474
  console.log('Type your message, or "exit" to quit.');
1388
-
1475
+
1389
1476
  rl.setPrompt('> ');
1390
1477
  rl.prompt();
1391
-
1478
+
1392
1479
  rl.on('line', async (input) => {
1393
1480
  if (input.toLowerCase().startsWith('/model')) {
1394
1481
  const newModel = input.split(' ')[1];
@@ -1441,11 +1528,99 @@ async function chat(rl, useToolCalling, initialModel) {
1441
1528
  return;
1442
1529
  }
1443
1530
 
1531
+ // Brainstorm command: /brainstorm <project-name> <description>
1532
+ if (input.toLowerCase().startsWith('/brainstorm')) {
1533
+ const parts = input.substring('/brainstorm'.length).trim();
1534
+ if (!parts) {
1535
+ console.log('Usage: /brainstorm <project-name> "<description>"');
1536
+ console.log('Example: /brainstorm "My Project" "A cool project that does things"');
1537
+ rl.prompt();
1538
+ return;
1539
+ }
1540
+
1541
+ // Parse project name and description
1542
+ const match = parts.match(/^"?([^"]+)"?\s+"?([^"]+)"?$/) || parts.match(/^(\S+)\s+(.+)$/);
1543
+ if (!match) {
1544
+ console.log('Usage: /brainstorm <project-name> "<description>"');
1545
+ rl.prompt();
1546
+ return;
1547
+ }
1548
+
1549
+ const projectName = match[1].trim();
1550
+ const projectDescription = match[2].trim();
1551
+ const outputDir = path.join(process.cwd(), projectName.replace(/\s+/g, '-').toLowerCase());
1552
+
1553
+ try {
1554
+ ui.showInfo(`Creating brainstorm session for "${projectName}"...`);
1555
+ const session = await brainstormCore.quickStart(
1556
+ projectName,
1557
+ projectDescription,
1558
+ outputDir,
1559
+ ['CLAUDE-1'] // Single agent by default
1560
+ );
1561
+
1562
+ ui.showSuccess(`✅ Brainstorm session created!`);
1563
+ console.log(` Session ID: ${session.id}`);
1564
+ console.log(` Output: ${outputDir}`);
1565
+ console.log(` Files generated: ${Object.keys(session.fileVersions).length}`);
1566
+ console.log(`\n Generated files:`);
1567
+ Object.keys(session.fileVersions).forEach(f => console.log(` - ${f}`));
1568
+ console.log(`\n Use "/finish <summary>" when done to complete the session.`);
1569
+
1570
+ // Store current session path for /finish command
1571
+ global.currentSessionDir = outputDir;
1572
+ } catch (error) {
1573
+ ui.showError(`Failed to create brainstorm: ${error.message}`);
1574
+ }
1575
+
1576
+ rl.prompt();
1577
+ return;
1578
+ }
1579
+
1580
+ // Finish command: /finish <summary>
1581
+ if (input.toLowerCase().startsWith('/finish')) {
1582
+ const summary = input.substring('/finish'.length).trim();
1583
+
1584
+ if (!global.currentSessionDir) {
1585
+ console.log('No active brainstorm session. Use /brainstorm first.');
1586
+ rl.prompt();
1587
+ return;
1588
+ }
1589
+
1590
+ if (!summary) {
1591
+ console.log('Usage: /finish "<summary of what was accomplished>"');
1592
+ rl.prompt();
1593
+ return;
1594
+ }
1595
+
1596
+ try {
1597
+ ui.showInfo('Completing brainstorm session...');
1598
+ const result = await brainstormCore.finishBrainstorm({
1599
+ sessionDir: global.currentSessionDir,
1600
+ summary,
1601
+ actor: 'CLAUDE-1'
1602
+ });
1603
+
1604
+ if (result.success) {
1605
+ ui.showSuccess(`✅ Session completed!`);
1606
+ console.log(` Summary: ${summary}`);
1607
+ global.currentSessionDir = null;
1608
+ } else {
1609
+ ui.showError(`Failed: ${result.errors.join(', ')}`);
1610
+ }
1611
+ } catch (error) {
1612
+ ui.showError(`Failed to finish session: ${error.message}`);
1613
+ }
1614
+
1615
+ rl.prompt();
1616
+ return;
1617
+ }
1618
+
1444
1619
  if (input.toLowerCase() === 'exit') {
1445
1620
  rl.close();
1446
1621
  return;
1447
1622
  }
1448
-
1623
+
1449
1624
  // Direct Harmony tool-call execution from user input (bypass model)
1450
1625
  try {
1451
1626
  const seg = parseSegmentedTranscript(input);
@@ -1476,16 +1651,24 @@ async function chat(rl, useToolCalling, initialModel) {
1476
1651
  } catch (e) {
1477
1652
  // Fall through to normal processing if parsing/execution fails
1478
1653
  }
1479
-
1480
- const result = useToolCalling
1481
- ? await processQueryWithTools(input, conversation, currentModel)
1482
- : await processQuery(input, conversation, currentModel);
1483
- ui.stopThinking();
1484
- ui.showResponse(result.response);
1485
-
1486
- conversation.length = 0;
1487
- result.conversation.forEach(msg => conversation.push(msg));
1488
-
1654
+
1655
+ try {
1656
+ const result = useToolCalling
1657
+ ? await processQueryWithTools(input, conversation, currentModel)
1658
+ : await processQuery(input, conversation, currentModel);
1659
+ ui.stopThinking();
1660
+ ui.showResponse(result.response);
1661
+
1662
+ conversation.length = 0;
1663
+ result.conversation.forEach(msg => conversation.push(msg));
1664
+ } catch (error) {
1665
+ ui.stopThinking();
1666
+ console.error('❌ Error processing request:', error.message);
1667
+ if (process.env.DEBUG) {
1668
+ console.error(error.stack);
1669
+ }
1670
+ }
1671
+
1489
1672
  rl.prompt();
1490
1673
  }).on('close', () => {
1491
1674
  ui.showResponse('Goodbye!');
@@ -1495,8 +1678,15 @@ async function chat(rl, useToolCalling, initialModel) {
1495
1678
 
1496
1679
  function askForMode(rl) {
1497
1680
  return new Promise((resolve) => {
1498
- rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
1499
- resolve(answer.trim() === '1');
1681
+ rl.question('Select mode (1 for tool calling, 2 for function calling, 3 for engineer): ', (answer) => {
1682
+ const mode = answer.trim();
1683
+ if (mode === '3') {
1684
+ resolve('engineer');
1685
+ } else if (mode === '2') {
1686
+ resolve('function');
1687
+ } else {
1688
+ resolve('tool');
1689
+ }
1500
1690
  });
1501
1691
  });
1502
1692
  }
@@ -1579,11 +1769,11 @@ async function start() {
1579
1769
 
1580
1770
  // Check if animation should be shown (can be disabled via config)
1581
1771
  const showAnimation = config.showAnimation !== false; // Default to true
1582
-
1772
+
1583
1773
  if (showAnimation && !process.env.SKIP_ANIMATION) {
1584
1774
  // Run the epic AGI awakening animation
1585
1775
  await runAGIAnimation();
1586
-
1776
+
1587
1777
  // Small pause after animation
1588
1778
  await new Promise(resolve => setTimeout(resolve, 500));
1589
1779
  }
@@ -1592,11 +1782,13 @@ async function start() {
1592
1782
  console.log('Select Mode:');
1593
1783
  console.log('1. Tool Calling (for models that support it)');
1594
1784
  console.log('2. Function Calling (legacy)');
1595
-
1596
- const useToolCalling = await askForMode(rl);
1597
- ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
1598
-
1599
- await chat(rl, useToolCalling, MODEL);
1785
+ console.log('3. Engineer Mode (autonomous engineering with Claude Code style editing)');
1786
+
1787
+ const selectedMode = await askForMode(rl);
1788
+ const modeNames = { 'tool': 'Tool Calling', 'function': 'Function Calling', 'engineer': 'Engineer' };
1789
+ ui.showResponse(`\nStarting in ${modeNames[selectedMode]} mode...\n`);
1790
+
1791
+ await chat(rl, selectedMode, MODEL);
1600
1792
  } catch (error) {
1601
1793
  ui.showError(error);
1602
1794
  rl.close();