sam-coder-cli 1.0.7 → 1.0.9

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.
Files changed (2) hide show
  1. package/bin/agi-cli.js +94 -181
  2. package/package.json +1 -1
package/bin/agi-cli.js CHANGED
@@ -12,7 +12,7 @@ const execAsync = util.promisify(exec);
12
12
  // Configuration
13
13
  const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
14
14
  let OPENROUTER_API_KEY;
15
- const MODEL = 'deepseek/deepseek-chat-v3-0324:free';
15
+ let MODEL = 'deepseek/deepseek-chat-v3-0324:free';
16
16
  let API_BASE_URL = 'https://openrouter.ai/api/v1';
17
17
 
18
18
  // Tool/Function definitions for the AI
@@ -380,19 +380,18 @@ function extractJsonFromMarkdown(text) {
380
380
  }
381
381
 
382
382
  // Call OpenRouter API with tool calling
383
- async function callOpenRouterWithTools(messages) {
383
+ async function callOpenRouter(messages, currentModel, useJson = false) {
384
384
  const apiKey = OPENROUTER_API_KEY;
385
385
 
386
386
  try {
387
- const response = await fetch(API_BASE_URL + '/chat/completions', {
387
+ const response = await fetch(`${API_BASE_URL}/chat/completions`, {
388
388
  method: 'POST',
389
389
  headers: {
390
- 'Content-Type': 'application/json',
391
- 'Authorization': `Bearer ${apiKey}`,
392
- 'HTTP-Referer': 'https://github.com/yourusername/agi-cli'
390
+ 'Authorization': `Bearer ${OPENROUTER_API_KEY}`,
391
+ 'Content-Type': 'application/json'
393
392
  },
394
393
  body: JSON.stringify({
395
- model: MODEL,
394
+ model: currentModel,
396
395
  messages: messages,
397
396
  tools: tools,
398
397
  tool_choice: 'auto'
@@ -415,100 +414,8 @@ async function callOpenRouterWithTools(messages) {
415
414
  }
416
415
  }
417
416
 
418
- // Call OpenRouter API with function calling (legacy)
419
- async function callOpenRouterWithFunctions(messages) {
420
- const apiKey = OPENROUTER_API_KEY;
421
-
422
- try {
423
- const response = await fetch(API_BASE_URL + '/chat/completions', {
424
- method: 'POST',
425
- headers: {
426
- 'Content-Type': 'application/json',
427
- 'Authorization': `Bearer ${apiKey}`,
428
- 'HTTP-Referer': 'https://github.com/yourusername/agi-cli'
429
- },
430
- body: JSON.stringify({
431
- model: MODEL,
432
- messages: messages
433
- })
434
- });
435
-
436
- if (!response.ok) {
437
- if (response.status === 401) {
438
- throw new Error('AuthenticationError: Invalid API key. Please run /setup to reconfigure.');
439
- }
440
- const error = await response.json();
441
- throw new Error(`API error: ${error.error?.message || response.statusText}`);
442
- }
443
-
444
- return await response.json();
445
- } catch (error) {
446
- console.error('API call failed:', error);
447
- ui.stopThinking();
448
- throw new Error(`Failed to call OpenRouter API: ${error.message}`);
449
- }
450
- }
451
-
452
- // Process tool calls from the AI response
453
- async function handleToolCalls(toolCalls, messages) {
454
- const results = [];
455
-
456
- for (const toolCall of toolCalls) {
457
- const functionName = toolCall.function.name;
458
- let args;
459
-
460
- try {
461
- args = JSON.parse(toolCall.function.arguments);
462
- } catch (error) {
463
- console.error('❌ Failed to parse tool arguments:', error);
464
- results.push({
465
- tool_call_id: toolCall.id,
466
- role: 'tool',
467
- name: functionName,
468
- content: JSON.stringify({ error: `Invalid arguments format: ${error.message}` })
469
- });
470
- continue;
471
- }
472
-
473
- console.log(`🔧 Executing ${functionName} with args:`, args);
474
-
475
- try {
476
- if (!agentUtils[functionName]) {
477
- throw new Error(`Tool '${functionName}' not found`);
478
- }
479
-
480
- const result = await agentUtils[functionName](...Object.values(args));
481
- console.log('✅ Tool executed successfully');
482
-
483
- // Stringify the result if it's not already a string
484
- const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
485
-
486
- results.push({
487
- tool_call_id: toolCall.id,
488
- role: 'tool',
489
- name: functionName,
490
- content: resultContent
491
- });
492
- } catch (error) {
493
- console.error('❌ Tool execution failed:', error);
494
-
495
- results.push({
496
- tool_call_id: toolCall.id,
497
- role: 'tool',
498
- name: functionName,
499
- content: JSON.stringify({
500
- error: error.message,
501
- stack: process.env.DEBUG ? error.stack : undefined
502
- })
503
- });
504
- }
505
- }
506
-
507
- return results;
508
- }
509
-
510
417
  // Process a query with tool calling
511
- async function processQueryWithTools(query, conversation = []) {
418
+ async function processQueryWithTools(query, conversation = [], currentModel) {
512
419
  // Add user message to conversation
513
420
  const userMessage = { role: 'user', content: query };
514
421
  const messages = [...conversation, userMessage];
@@ -527,7 +434,7 @@ If a tool fails, you can try again with different parameters or a different appr
527
434
  ui.startThinking();
528
435
 
529
436
  try {
530
- const response = await callOpenRouterWithTools(messages);
437
+ const response = await callOpenRouterWithTools(messages, currentModel);
531
438
  const assistantMessage = response.choices[0].message;
532
439
  messages.push(assistantMessage);
533
440
 
@@ -550,7 +457,7 @@ If a tool fails, you can try again with different parameters or a different appr
550
457
 
551
458
  // Now, get the AI's response to the tool results
552
459
  ui.startThinking();
553
- const finalResponseObj = await callOpenRouterWithTools(messages);
460
+ const finalResponseObj = await callOpenRouterWithTools(messages, currentModel);
554
461
  const finalAssistantMessage = finalResponseObj.choices[0].message;
555
462
  messages.push(finalAssistantMessage);
556
463
  ui.stopThinking();
@@ -570,53 +477,8 @@ If a tool fails, you can try again with different parameters or a different appr
570
477
  }
571
478
  }
572
479
 
573
- // Execute a single action from the action system
574
- async function executeAction(action) {
575
- const { type, data } = action;
576
-
577
- switch (type) {
578
- case 'read':
579
- return await agentUtils.readFile(data.path);
580
-
581
- case 'write':
582
- return await agentUtils.writeFile(data.path, data.content);
583
-
584
- case 'edit':
585
- return await agentUtils.editFile(data.path, data.edits);
586
-
587
- case 'command':
588
- return await agentUtils.runCommand(data.command);
589
-
590
- case 'search':
591
- if (data.type === 'files') {
592
- return await agentUtils.searchFiles(data.pattern);
593
- }
594
- throw new Error('Text search is not implemented yet');
595
-
596
- case 'execute':
597
- // For execute action, we'll run it as a command
598
- const cmd = data.language === 'bash'
599
- ? data.code
600
- : `node -e "${data.code.replace(/"/g, '\\"')}"`;
601
- return await agentUtils.runCommand(cmd);
602
-
603
- case 'browse':
604
- throw new Error('Web browsing is not implemented yet');
605
-
606
- case 'analyze':
607
- // For analyze action, we'll just return the question for now
608
- return `Analysis requested for code: ${data.code}\nQuestion: ${data.question}`;
609
-
610
- case 'stop':
611
- return 'Stopping action execution';
612
-
613
- default:
614
- throw new Error(`Unknown action type: ${type}`);
615
- }
616
- }
617
-
618
480
  // Process a query with action handling (legacy function calling)
619
- async function processQuery(query, conversation = []) {
481
+ async function processQuery(query, conversation = [], currentModel) {
620
482
  try {
621
483
  // Add user message to conversation
622
484
  const userMessage = { role: 'user', content: query };
@@ -632,10 +494,10 @@ async function processQuery(query, conversation = []) {
632
494
 
633
495
  ui.startThinking();
634
496
 
635
- const response = await callOpenRouterWithFunctions(messages);
497
+ const response = await callOpenRouterWithFunctions(messages, currentModel);
636
498
  const assistantMessage = response.choices[0].message;
637
499
  messages.push(assistantMessage);
638
-
500
+
639
501
  // Try to extract JSON from the response
640
502
  const actionData = extractJsonFromMarkdown(assistantMessage.content);
641
503
 
@@ -674,31 +536,51 @@ async function processQuery(query, conversation = []) {
674
536
  };
675
537
  } catch (error) {
676
538
  ui.stopThinking();
677
- ui.showError(`Error processing query: ${error.message}`);
539
+ console.error('❌ Error during processing:', error);
678
540
  return {
679
- response: `Error: ${error.message}`,
680
- conversation: conversation || []
541
+ response: `An error occurred: ${error.message}`,
542
+ conversation: messages
681
543
  };
682
544
  }
683
545
  }
684
546
 
685
547
  // Main chat loop
686
- async function chat(useToolCalling) {
548
+ async function chat(rl, useToolCalling, initialModel) {
549
+ let currentModel = initialModel;
687
550
  const conversation = [];
688
- ui.showHeader();
689
551
  console.log('Type your message, or "exit" to quit.');
690
552
 
691
- const rl = readline.createInterface({
692
- input: process.stdin,
693
- output: process.stdout,
694
- prompt: '> '
695
- });
696
-
553
+ rl.setPrompt('> ');
697
554
  rl.prompt();
698
555
 
699
556
  rl.on('line', async (input) => {
557
+ if (input.toLowerCase().startsWith('/model')) {
558
+ const newModel = input.split(' ')[1];
559
+ if (newModel) {
560
+ currentModel = newModel;
561
+ let config = await readConfig();
562
+ config.MODEL = currentModel;
563
+ await writeConfig(config);
564
+ console.log(`Model changed to: ${currentModel}`);
565
+ } else {
566
+ console.log('Please specify a model. Usage: /model <model_name>');
567
+ }
568
+ rl.prompt();
569
+ return;
570
+ }
571
+
572
+ if (input.toLowerCase() === '/default-model') {
573
+ currentModel = 'deepseek/deepseek-chat-v3-0324:free';
574
+ let config = await readConfig();
575
+ config.MODEL = currentModel;
576
+ await writeConfig(config);
577
+ console.log(`Model reset to default: ${currentModel}`);
578
+ rl.prompt();
579
+ return;
580
+ }
581
+
700
582
  if (input.toLowerCase() === '/setup') {
701
- await runSetup(true);
583
+ await runSetup(rl, true);
702
584
  console.log('\nSetup complete. Please restart the application to apply changes.');
703
585
  rl.close();
704
586
  return;
@@ -710,8 +592,8 @@ async function chat(useToolCalling) {
710
592
  }
711
593
 
712
594
  const result = useToolCalling
713
- ? await processQueryWithTools(input, conversation)
714
- : await processQuery(input, conversation);
595
+ ? await processQueryWithTools(input, conversation, currentModel)
596
+ : await processQuery(input, conversation, currentModel);
715
597
  ui.stopThinking();
716
598
  ui.showResponse(result.response);
717
599
 
@@ -727,15 +609,9 @@ async function chat(useToolCalling) {
727
609
  }
728
610
 
729
611
  // Ask user for mode selection
730
- function askForMode() {
731
- const rl = readline.createInterface({
732
- input: process.stdin,
733
- output: process.stdout
734
- });
735
-
612
+ function askForMode(rl) {
736
613
  return new Promise((resolve) => {
737
614
  rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
738
- rl.close();
739
615
  resolve(answer.trim() === '1');
740
616
  });
741
617
  });
@@ -758,12 +634,7 @@ async function writeConfig(config) {
758
634
  await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
759
635
  }
760
636
 
761
- async function runSetup(isReconfig = false) {
762
- const rl = readline.createInterface({
763
- input: process.stdin,
764
- output: process.stdout
765
- });
766
-
637
+ async function runSetup(rl, isReconfig = false) {
767
638
  const askQuestion = (query) => new Promise(resolve => rl.question(query, resolve));
768
639
 
769
640
  if (!isReconfig) {
@@ -797,18 +668,24 @@ async function runSetup(isReconfig = false) {
797
668
  console.log('✅ Configuration saved successfully!');
798
669
  }
799
670
 
800
- rl.close();
801
671
  return config;
802
672
  }
803
673
 
804
674
  // Start the application
805
675
  async function start() {
676
+ const rl = readline.createInterface({
677
+ input: process.stdin,
678
+ output: process.stdout
679
+ });
680
+
806
681
  try {
807
682
  let config = await readConfig();
808
683
  if (!config || !config.OPENROUTER_API_KEY) {
809
- config = await runSetup();
684
+ config = await runSetup(rl);
810
685
  }
811
686
 
687
+ MODEL = config.MODEL || 'deepseek/deepseek-chat-v3-0324:free'; // Load model from config or use default
688
+
812
689
  OPENROUTER_API_KEY = config.OPENROUTER_API_KEY;
813
690
  if (config.isPro && config.customApiBase) {
814
691
  API_BASE_URL = config.customApiBase;
@@ -820,13 +697,49 @@ async function start() {
820
697
  console.log('1. Tool Calling (for models that support it)');
821
698
  console.log('2. Function Calling (legacy)');
822
699
 
823
- const useToolCalling = await askForMode();
700
+ const useToolCalling = await askForMode(rl);
824
701
  ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
825
702
 
826
703
  // Start the chat with the selected mode
827
- await chat(useToolCalling);
704
+ await chat(rl, useToolCalling, MODEL);
828
705
  } catch (error) {
829
706
  ui.showError(error);
707
+ rl.close();
708
+ process.exit(1);
709
+ }
710
+ }
711
+ const rl = readline.createInterface({
712
+ input: process.stdin,
713
+ output: process.stdout
714
+ });
715
+
716
+ try {
717
+ let config = await readConfig();
718
+ if (!config || !config.OPENROUTER_API_KEY) {
719
+ config = await runSetup(rl);
720
+ }
721
+
722
+ MODEL = config.MODEL || 'deepseek/deepseek-chat-v3-0324:free'; // Load model from config or use default
723
+
724
+ OPENROUTER_API_KEY = config.OPENROUTER_API_KEY;
725
+ if (config.isPro && config.customApiBase) {
726
+ API_BASE_URL = config.customApiBase;
727
+ console.log(`🚀 Using Pro Plan custom endpoint: ${API_BASE_URL}`);
728
+ }
729
+
730
+ ui.showHeader();
731
+ console.log('Select Mode:');
732
+ console.log('1. Tool Calling (for models that support it)');
733
+ console.log('2. Function Calling (legacy)');
734
+
735
+ const useToolCalling = await askForMode(rl);
736
+ ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
737
+
738
+ // Start the chat with the selected mode
739
+ await chat(rl, useToolCalling, MODEL);
740
+ } catch (error) {
741
+ ui.showError(error);
742
+ rl.close();
830
743
  process.exit(1);
831
744
  }
832
745
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {