sam-coder-cli 1.0.8 → 1.0.10
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 +104 -166
- 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
|
-
|
|
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,22 +380,21 @@ function extractJsonFromMarkdown(text) {
|
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
// Call OpenRouter API with tool calling
|
|
383
|
-
async function
|
|
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
|
|
387
|
+
const response = await fetch(`${API_BASE_URL}/chat/completions`, {
|
|
388
388
|
method: 'POST',
|
|
389
389
|
headers: {
|
|
390
|
-
'Content-Type': 'application/json',
|
|
391
390
|
'Authorization': `Bearer ${apiKey}`,
|
|
392
|
-
'
|
|
391
|
+
'Content-Type': 'application/json'
|
|
393
392
|
},
|
|
394
393
|
body: JSON.stringify({
|
|
395
|
-
model:
|
|
394
|
+
model: currentModel,
|
|
396
395
|
messages: messages,
|
|
397
|
-
tools: tools,
|
|
398
|
-
tool_choice: 'auto'
|
|
396
|
+
tools: useJson ? undefined : tools,
|
|
397
|
+
tool_choice: useJson ? undefined : 'auto'
|
|
399
398
|
})
|
|
400
399
|
});
|
|
401
400
|
|
|
@@ -415,41 +414,49 @@ async function callOpenRouterWithTools(messages) {
|
|
|
415
414
|
}
|
|
416
415
|
}
|
|
417
416
|
|
|
418
|
-
//
|
|
419
|
-
async function
|
|
420
|
-
const
|
|
417
|
+
// Process a query with tool calling
|
|
418
|
+
async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
419
|
+
const userMessage = { role: 'user', content: query };
|
|
420
|
+
const messages = [...conversation, userMessage];
|
|
421
|
+
|
|
422
|
+
ui.startThinking();
|
|
421
423
|
|
|
422
424
|
try {
|
|
423
|
-
const response = await
|
|
424
|
-
|
|
425
|
-
|
|
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
|
-
});
|
|
425
|
+
const response = await callOpenRouter(messages, currentModel);
|
|
426
|
+
const assistantMessage = response.choices[0].message;
|
|
427
|
+
messages.push(assistantMessage);
|
|
435
428
|
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
const error = await response.json();
|
|
441
|
-
throw new Error(`API error: ${error.error?.message || response.statusText}`);
|
|
442
|
-
}
|
|
429
|
+
if (assistantMessage.tool_calls) {
|
|
430
|
+
const toolResults = await handleToolCalls(assistantMessage.tool_calls, messages);
|
|
431
|
+
messages.push(...toolResults);
|
|
443
432
|
|
|
444
|
-
|
|
433
|
+
ui.startThinking();
|
|
434
|
+
const finalResponseObj = await callOpenRouter(messages, currentModel);
|
|
435
|
+
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
436
|
+
messages.push(finalAssistantMessage);
|
|
437
|
+
ui.stopThinking();
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
response: finalAssistantMessage.content,
|
|
441
|
+
conversation: messages
|
|
442
|
+
};
|
|
443
|
+
} else {
|
|
444
|
+
ui.stopThinking();
|
|
445
|
+
return {
|
|
446
|
+
response: assistantMessage.content,
|
|
447
|
+
conversation: messages
|
|
448
|
+
};
|
|
449
|
+
}
|
|
445
450
|
} catch (error) {
|
|
446
|
-
console.error('API call failed:', error);
|
|
447
451
|
ui.stopThinking();
|
|
448
|
-
|
|
452
|
+
ui.showError(`Error processing query: ${error.message}`);
|
|
453
|
+
return {
|
|
454
|
+
response: `Error: ${error.message}`,
|
|
455
|
+
conversation: messages
|
|
456
|
+
};
|
|
449
457
|
}
|
|
450
458
|
}
|
|
451
459
|
|
|
452
|
-
// Process tool calls from the AI response
|
|
453
460
|
async function handleToolCalls(toolCalls, messages) {
|
|
454
461
|
const results = [];
|
|
455
462
|
|
|
@@ -480,7 +487,6 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
480
487
|
const result = await agentUtils[functionName](...Object.values(args));
|
|
481
488
|
console.log('✅ Tool executed successfully');
|
|
482
489
|
|
|
483
|
-
// Stringify the result if it's not already a string
|
|
484
490
|
const resultContent = typeof result === 'string' ? result : JSON.stringify(result);
|
|
485
491
|
|
|
486
492
|
results.push({
|
|
@@ -507,183 +513,94 @@ async function handleToolCalls(toolCalls, messages) {
|
|
|
507
513
|
return results;
|
|
508
514
|
}
|
|
509
515
|
|
|
510
|
-
// Process a query with tool calling
|
|
511
|
-
async function processQueryWithTools(query, conversation = []) {
|
|
512
|
-
// Add user message to conversation
|
|
513
|
-
const userMessage = { role: 'user', content: query };
|
|
514
|
-
const messages = [...conversation, userMessage];
|
|
515
|
-
|
|
516
|
-
// Add system message if this is the first message
|
|
517
|
-
if (conversation.length === 0) {
|
|
518
|
-
messages.unshift({
|
|
519
|
-
role: 'system',
|
|
520
|
-
content: `You are a helpful AI assistant with access to tools. Use the tools when needed.
|
|
521
|
-
You can use multiple tools in sequence if needed to complete the task.
|
|
522
|
-
When using tools, make sure to provide all required parameters.
|
|
523
|
-
If a tool fails, you can try again with different parameters or a different approach.`
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
ui.startThinking();
|
|
528
|
-
|
|
529
|
-
try {
|
|
530
|
-
const response = await callOpenRouterWithTools(messages);
|
|
531
|
-
const assistantMessage = response.choices[0].message;
|
|
532
|
-
messages.push(assistantMessage);
|
|
533
|
-
|
|
534
|
-
// If there are no tool calls, we're done
|
|
535
|
-
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
536
|
-
ui.stopThinking();
|
|
537
|
-
return {
|
|
538
|
-
response: assistantMessage.content || 'No content in response',
|
|
539
|
-
conversation: messages
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Process tool calls
|
|
544
|
-
ui.stopThinking(); // Stop thinking while tools execute
|
|
545
|
-
console.log(`🛠️ Executing ${assistantMessage.tool_calls.length} tools...`);
|
|
546
|
-
const toolResults = await handleToolCalls(assistantMessage.tool_calls, messages);
|
|
547
|
-
|
|
548
|
-
// Add tool results to messages
|
|
549
|
-
messages.push(...toolResults);
|
|
550
|
-
|
|
551
|
-
// Now, get the AI's response to the tool results
|
|
552
|
-
ui.startThinking();
|
|
553
|
-
const finalResponseObj = await callOpenRouterWithTools(messages);
|
|
554
|
-
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
555
|
-
messages.push(finalAssistantMessage);
|
|
556
|
-
ui.stopThinking();
|
|
557
|
-
|
|
558
|
-
return {
|
|
559
|
-
response: finalAssistantMessage.content || 'Actions executed. What is the next step?',
|
|
560
|
-
conversation: messages
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
} catch (error) {
|
|
564
|
-
ui.stopThinking();
|
|
565
|
-
console.error('❌ Error during processing:', error);
|
|
566
|
-
return {
|
|
567
|
-
response: `An error occurred: ${error.message}`,
|
|
568
|
-
conversation: messages
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Execute a single action from the action system
|
|
574
516
|
async function executeAction(action) {
|
|
575
517
|
const { type, data } = action;
|
|
576
518
|
|
|
577
519
|
switch (type) {
|
|
578
520
|
case 'read':
|
|
579
521
|
return await agentUtils.readFile(data.path);
|
|
580
|
-
|
|
581
522
|
case 'write':
|
|
582
523
|
return await agentUtils.writeFile(data.path, data.content);
|
|
583
|
-
|
|
584
524
|
case 'edit':
|
|
585
525
|
return await agentUtils.editFile(data.path, data.edits);
|
|
586
|
-
|
|
587
526
|
case 'command':
|
|
588
527
|
return await agentUtils.runCommand(data.command);
|
|
589
|
-
|
|
590
528
|
case 'search':
|
|
591
529
|
if (data.type === 'files') {
|
|
592
530
|
return await agentUtils.searchFiles(data.pattern);
|
|
593
531
|
}
|
|
594
532
|
throw new Error('Text search is not implemented yet');
|
|
595
|
-
|
|
596
533
|
case 'execute':
|
|
597
|
-
// For execute action, we'll run it as a command
|
|
598
534
|
const cmd = data.language === 'bash'
|
|
599
535
|
? data.code
|
|
600
|
-
: `node -e "${data.code.replace(/"/g, '
|
|
536
|
+
: `node -e "${data.code.replace(/"/g, '\"')}"`;
|
|
601
537
|
return await agentUtils.runCommand(cmd);
|
|
602
|
-
|
|
603
538
|
case 'browse':
|
|
604
539
|
throw new Error('Web browsing is not implemented yet');
|
|
605
|
-
|
|
606
540
|
case 'analyze':
|
|
607
|
-
// For analyze action, we'll just return the question for now
|
|
608
541
|
return `Analysis requested for code: ${data.code}\nQuestion: ${data.question}`;
|
|
609
|
-
|
|
610
542
|
case 'stop':
|
|
611
543
|
return 'Stopping action execution';
|
|
612
|
-
|
|
613
544
|
default:
|
|
614
545
|
throw new Error(`Unknown action type: ${type}`);
|
|
615
546
|
}
|
|
616
547
|
}
|
|
617
548
|
|
|
618
|
-
|
|
619
|
-
async function processQuery(query, conversation = []) {
|
|
549
|
+
async function processQuery(query, conversation = [], currentModel) {
|
|
620
550
|
try {
|
|
621
|
-
// Add user message to conversation
|
|
622
551
|
const userMessage = { role: 'user', content: query };
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (conversation.length === 0) {
|
|
627
|
-
messages.unshift({
|
|
628
|
-
role: 'system',
|
|
629
|
-
content: FUNCTION_CALLING_PROMPT
|
|
630
|
-
});
|
|
631
|
-
}
|
|
552
|
+
let messages = [...conversation, userMessage];
|
|
553
|
+
let actionCount = 0;
|
|
554
|
+
const MAX_ACTIONS = 10;
|
|
632
555
|
|
|
633
556
|
ui.startThinking();
|
|
634
557
|
|
|
635
|
-
const
|
|
636
|
-
const assistantMessage =
|
|
558
|
+
const responseText = await callOpenRouter(messages, currentModel, true);
|
|
559
|
+
const assistantMessage = responseText.choices[0].message;
|
|
637
560
|
messages.push(assistantMessage);
|
|
638
561
|
|
|
639
|
-
// Try to extract JSON from the response
|
|
640
562
|
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
641
563
|
|
|
642
|
-
if (actionData
|
|
564
|
+
if (!actionData || actionData.type === 'stop') {
|
|
643
565
|
ui.stopThinking();
|
|
644
|
-
ui.showAction('Executing actions...');
|
|
645
|
-
const results = [];
|
|
646
|
-
|
|
647
|
-
for (const action of actionData.actions) {
|
|
648
|
-
ui.showAction(` → ${action.type} action`);
|
|
649
|
-
try {
|
|
650
|
-
const result = await executeAction(action);
|
|
651
|
-
results.push({ type: action.type, success: true, result });
|
|
652
|
-
} catch (error) {
|
|
653
|
-
results.push({ type: action.type, success: false, error: error.message });
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Add action results to the conversation
|
|
658
|
-
messages.push({
|
|
659
|
-
role: 'system',
|
|
660
|
-
content: `Action results: ${JSON.stringify(results, null, 2)}`
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
// Continue the conversation with the results
|
|
664
566
|
return {
|
|
665
|
-
response:
|
|
567
|
+
response: assistantMessage.content,
|
|
666
568
|
conversation: messages
|
|
667
569
|
};
|
|
668
570
|
}
|
|
669
|
-
|
|
571
|
+
|
|
572
|
+
let currentAction = actionData;
|
|
573
|
+
while (currentAction && currentAction.type !== 'stop' && actionCount < MAX_ACTIONS) {
|
|
574
|
+
actionCount++;
|
|
575
|
+
const result = await executeAction(currentAction);
|
|
576
|
+
|
|
577
|
+
messages.push({ role: 'assistant', content: `Action result: ${result}` });
|
|
578
|
+
|
|
579
|
+
ui.startThinking();
|
|
580
|
+
const nextResponse = await callOpenRouter(messages, currentModel, true);
|
|
581
|
+
const nextAssistantMessage = nextResponse.choices[0].message;
|
|
582
|
+
messages.push(nextAssistantMessage);
|
|
583
|
+
|
|
584
|
+
currentAction = extractJsonFromMarkdown(nextAssistantMessage.content);
|
|
585
|
+
}
|
|
586
|
+
|
|
670
587
|
ui.stopThinking();
|
|
671
588
|
return {
|
|
672
|
-
response:
|
|
589
|
+
response: messages[messages.length - 1].content,
|
|
673
590
|
conversation: messages
|
|
674
591
|
};
|
|
675
592
|
} catch (error) {
|
|
676
593
|
ui.stopThinking();
|
|
677
|
-
|
|
594
|
+
console.error('❌ Error during processing:', error);
|
|
678
595
|
return {
|
|
679
|
-
response: `
|
|
680
|
-
conversation: conversation
|
|
596
|
+
response: `An error occurred: ${error.message}`,
|
|
597
|
+
conversation: conversation
|
|
681
598
|
};
|
|
682
599
|
}
|
|
683
600
|
}
|
|
684
601
|
|
|
685
|
-
|
|
686
|
-
|
|
602
|
+
async function chat(rl, useToolCalling, initialModel) {
|
|
603
|
+
let currentModel = initialModel;
|
|
687
604
|
const conversation = [];
|
|
688
605
|
console.log('Type your message, or "exit" to quit.');
|
|
689
606
|
|
|
@@ -691,6 +608,31 @@ async function chat(rl, useToolCalling) {
|
|
|
691
608
|
rl.prompt();
|
|
692
609
|
|
|
693
610
|
rl.on('line', async (input) => {
|
|
611
|
+
if (input.toLowerCase().startsWith('/model')) {
|
|
612
|
+
const newModel = input.split(' ')[1];
|
|
613
|
+
if (newModel) {
|
|
614
|
+
currentModel = newModel;
|
|
615
|
+
let config = await readConfig() || {};
|
|
616
|
+
config.MODEL = currentModel;
|
|
617
|
+
await writeConfig(config);
|
|
618
|
+
console.log(`Model changed to: ${currentModel}`);
|
|
619
|
+
} else {
|
|
620
|
+
console.log('Please specify a model. Usage: /model <model_name>');
|
|
621
|
+
}
|
|
622
|
+
rl.prompt();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (input.toLowerCase() === '/default-model') {
|
|
627
|
+
currentModel = 'deepseek/deepseek-chat-v3-0324:free';
|
|
628
|
+
let config = await readConfig() || {};
|
|
629
|
+
config.MODEL = currentModel;
|
|
630
|
+
await writeConfig(config);
|
|
631
|
+
console.log(`Model reset to default: ${currentModel}`);
|
|
632
|
+
rl.prompt();
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
694
636
|
if (input.toLowerCase() === '/setup') {
|
|
695
637
|
await runSetup(rl, true);
|
|
696
638
|
console.log('\nSetup complete. Please restart the application to apply changes.');
|
|
@@ -704,13 +646,12 @@ async function chat(rl, useToolCalling) {
|
|
|
704
646
|
}
|
|
705
647
|
|
|
706
648
|
const result = useToolCalling
|
|
707
|
-
? await processQueryWithTools(input, conversation)
|
|
708
|
-
: await processQuery(input, conversation);
|
|
649
|
+
? await processQueryWithTools(input, conversation, currentModel)
|
|
650
|
+
: await processQuery(input, conversation, currentModel);
|
|
709
651
|
ui.stopThinking();
|
|
710
652
|
ui.showResponse(result.response);
|
|
711
653
|
|
|
712
|
-
|
|
713
|
-
conversation.length = 0; // Clear the array
|
|
654
|
+
conversation.length = 0;
|
|
714
655
|
result.conversation.forEach(msg => conversation.push(msg));
|
|
715
656
|
|
|
716
657
|
rl.prompt();
|
|
@@ -720,7 +661,6 @@ async function chat(rl, useToolCalling) {
|
|
|
720
661
|
});
|
|
721
662
|
}
|
|
722
663
|
|
|
723
|
-
// Ask user for mode selection
|
|
724
664
|
function askForMode(rl) {
|
|
725
665
|
return new Promise((resolve) => {
|
|
726
666
|
rl.question('Select mode (1 for tool calling, 2 for function calling): ', (answer) => {
|
|
@@ -729,14 +669,13 @@ function askForMode(rl) {
|
|
|
729
669
|
});
|
|
730
670
|
}
|
|
731
671
|
|
|
732
|
-
// Start the application
|
|
733
672
|
async function readConfig() {
|
|
734
673
|
try {
|
|
735
674
|
const data = await fs.readFile(CONFIG_PATH, 'utf-8');
|
|
736
675
|
return JSON.parse(data);
|
|
737
676
|
} catch (error) {
|
|
738
677
|
if (error.code === 'ENOENT') {
|
|
739
|
-
return null;
|
|
678
|
+
return null;
|
|
740
679
|
}
|
|
741
680
|
throw error;
|
|
742
681
|
}
|
|
@@ -783,7 +722,6 @@ async function runSetup(rl, isReconfig = false) {
|
|
|
783
722
|
return config;
|
|
784
723
|
}
|
|
785
724
|
|
|
786
|
-
// Start the application
|
|
787
725
|
async function start() {
|
|
788
726
|
const rl = readline.createInterface({
|
|
789
727
|
input: process.stdin,
|
|
@@ -796,6 +734,8 @@ async function start() {
|
|
|
796
734
|
config = await runSetup(rl);
|
|
797
735
|
}
|
|
798
736
|
|
|
737
|
+
MODEL = config.MODEL || 'deepseek/deepseek-chat-v3-0324:free';
|
|
738
|
+
|
|
799
739
|
OPENROUTER_API_KEY = config.OPENROUTER_API_KEY;
|
|
800
740
|
if (config.isPro && config.customApiBase) {
|
|
801
741
|
API_BASE_URL = config.customApiBase;
|
|
@@ -810,8 +750,7 @@ async function start() {
|
|
|
810
750
|
const useToolCalling = await askForMode(rl);
|
|
811
751
|
ui.showResponse(`\nStarting in ${useToolCalling ? 'Tool Calling' : 'Function Calling'} mode...\n`);
|
|
812
752
|
|
|
813
|
-
|
|
814
|
-
await chat(rl, useToolCalling);
|
|
753
|
+
await chat(rl, useToolCalling, MODEL);
|
|
815
754
|
} catch (error) {
|
|
816
755
|
ui.showError(error);
|
|
817
756
|
rl.close();
|
|
@@ -819,5 +758,4 @@ async function start() {
|
|
|
819
758
|
}
|
|
820
759
|
}
|
|
821
760
|
|
|
822
|
-
// Start the application
|
|
823
761
|
start().catch(console.error);
|