sam-coder-cli 1.0.46 → 1.0.47
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 +250 -43
- package/package.json +1 -1
package/bin/agi-cli.js
CHANGED
|
@@ -211,11 +211,130 @@ INSTRUCTIONS:
|
|
|
211
211
|
Always think step by step and explain your reasoning before taking actions that could affect the system.`;
|
|
212
212
|
|
|
213
213
|
// System prompt for the AI Assistant when using legacy function calling (JSON actions)
|
|
214
|
-
const FUNCTION_CALLING_PROMPT = `You are a helpful AI assistant with agency capabilities. You can perform actions on the user's system.
|
|
214
|
+
const FUNCTION_CALLING_PROMPT = `You are a helpful AI assistant with agency capabilities. You can perform actions on the user's system using JSON-formatted actions.
|
|
215
|
+
|
|
216
|
+
AVAILABLE ACTIONS:
|
|
217
|
+
1. read - Read the contents of a file
|
|
218
|
+
2. write - Write content to a file
|
|
219
|
+
3. edit - Edit specific parts of a file
|
|
220
|
+
4. command - Execute a shell command
|
|
221
|
+
5. search - Search for files using patterns
|
|
222
|
+
6. execute - Execute code (bash or node.js)
|
|
223
|
+
7. analyze - Analyze code and answer questions
|
|
224
|
+
8. stop - Stop taking actions and provide final response
|
|
225
|
+
|
|
226
|
+
ACTION FORMAT:
|
|
227
|
+
You must respond with a JSON object wrapped in markdown code blocks with the 'json' language specifier.
|
|
228
|
+
The JSON should have the following structure:
|
|
229
|
+
{
|
|
230
|
+
"type": "action_name",
|
|
231
|
+
"data": { /* action-specific parameters */ },
|
|
232
|
+
"reasoning": "Brief explanation of why you're taking this action"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
EXAMPLES:
|
|
236
|
+
|
|
237
|
+
To read a file:
|
|
238
|
+
\`\`\`json
|
|
239
|
+
{
|
|
240
|
+
"type": "read",
|
|
241
|
+
"data": {
|
|
242
|
+
"path": "path/to/file.txt"
|
|
243
|
+
},
|
|
244
|
+
"reasoning": "Reading the file to understand its current contents"
|
|
245
|
+
}
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
To write a file:
|
|
249
|
+
\`\`\`json
|
|
250
|
+
{
|
|
251
|
+
"type": "write",
|
|
252
|
+
"data": {
|
|
253
|
+
"path": "path/to/file.txt",
|
|
254
|
+
"content": "File content here"
|
|
255
|
+
},
|
|
256
|
+
"reasoning": "Creating a new file with the specified content"
|
|
257
|
+
}
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
To edit a file:
|
|
261
|
+
\`\`\`json
|
|
262
|
+
{
|
|
263
|
+
"type": "edit",
|
|
264
|
+
"data": {
|
|
265
|
+
"path": "path/to/file.txt",
|
|
266
|
+
"edits": {
|
|
267
|
+
"operations": [
|
|
268
|
+
{
|
|
269
|
+
"type": "replace",
|
|
270
|
+
"startLine": 5,
|
|
271
|
+
"endLine": 7,
|
|
272
|
+
"newText": "New content for lines 5-7"
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"reasoning": "Modifying specific lines in the file"
|
|
278
|
+
}
|
|
279
|
+
\`\`\`
|
|
280
|
+
|
|
281
|
+
To run a command:
|
|
282
|
+
\`\`\`json
|
|
283
|
+
{
|
|
284
|
+
"type": "command",
|
|
285
|
+
"data": {
|
|
286
|
+
"command": "ls -la"
|
|
287
|
+
},
|
|
288
|
+
"reasoning": "Listing directory contents to see what files are available"
|
|
289
|
+
}
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
To search for files:
|
|
293
|
+
\`\`\`json
|
|
294
|
+
{
|
|
295
|
+
"type": "search",
|
|
296
|
+
"data": {
|
|
297
|
+
"type": "files",
|
|
298
|
+
"pattern": "*.js"
|
|
299
|
+
},
|
|
300
|
+
"reasoning": "Finding all JavaScript files in the current directory"
|
|
301
|
+
}
|
|
302
|
+
\`\`\`
|
|
303
|
+
|
|
304
|
+
To execute code:
|
|
305
|
+
\`\`\`json
|
|
306
|
+
{
|
|
307
|
+
"type": "execute",
|
|
308
|
+
"data": {
|
|
309
|
+
"language": "bash",
|
|
310
|
+
"code": "echo 'Hello World'"
|
|
311
|
+
},
|
|
312
|
+
"reasoning": "Running a simple bash command"
|
|
313
|
+
}
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
To stop and provide final response:
|
|
317
|
+
\`\`\`json
|
|
318
|
+
{
|
|
319
|
+
"type": "stop",
|
|
320
|
+
"data": {},
|
|
321
|
+
"reasoning": "Task completed successfully"
|
|
322
|
+
}
|
|
323
|
+
\`\`\`
|
|
215
324
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
325
|
+
IMPORTANT RULES:
|
|
326
|
+
- Always wrap your JSON in markdown code blocks with 'json' language specifier
|
|
327
|
+
- Include a "reasoning" field explaining why you're taking the action
|
|
328
|
+
- You will continue taking actions in a loop until you use the 'stop' action
|
|
329
|
+
- Be careful with destructive operations - explain what you're doing
|
|
330
|
+
- If an action fails, analyze the error and try a different approach
|
|
331
|
+
- Always think step by step before taking actions
|
|
332
|
+
|
|
333
|
+
ENVIRONMENT:
|
|
334
|
+
- OS: ${process.platform}
|
|
335
|
+
- Current directory: ${process.cwd()}
|
|
336
|
+
|
|
337
|
+
Start by understanding the user's request, then take appropriate actions to fulfill it.`;
|
|
219
338
|
|
|
220
339
|
// Agent utilities
|
|
221
340
|
const agentUtils = {
|
|
@@ -357,26 +476,68 @@ const agentUtils = {
|
|
|
357
476
|
|
|
358
477
|
// Extract JSON from markdown code blocks
|
|
359
478
|
function extractJsonFromMarkdown(text) {
|
|
360
|
-
|
|
361
|
-
|
|
479
|
+
if (!text || typeof text !== 'string') {
|
|
480
|
+
console.error('Invalid input to extractJsonFromMarkdown:', typeof text);
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Try to find a markdown code block with JSON content (case insensitive)
|
|
485
|
+
const codeBlockRegex = /```(?:json|JSON)\s*([\s\S]*?)\s*```/i;
|
|
362
486
|
const match = text.match(codeBlockRegex);
|
|
363
487
|
|
|
364
|
-
if (match) {
|
|
488
|
+
if (match && match[1]) {
|
|
365
489
|
try {
|
|
366
|
-
|
|
490
|
+
const jsonStr = match[1].trim();
|
|
491
|
+
if (!jsonStr) {
|
|
492
|
+
console.error('Empty JSON content in markdown block');
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return JSON.parse(jsonStr);
|
|
367
496
|
} catch (error) {
|
|
368
|
-
console.error('Error parsing JSON from markdown:', error);
|
|
497
|
+
console.error('Error parsing JSON from markdown block:', error.message);
|
|
498
|
+
console.error('JSON content was:', match[1]);
|
|
369
499
|
}
|
|
370
500
|
}
|
|
371
501
|
|
|
372
|
-
// If no code block,
|
|
502
|
+
// If no code block found, look for JSON-like patterns in the text
|
|
503
|
+
const jsonPatterns = [
|
|
504
|
+
// Look for objects that start with { and end with }
|
|
505
|
+
/\{[\s\S]*?\}/g,
|
|
506
|
+
// Look for arrays that start with [ and end with ]
|
|
507
|
+
/\[[\s\S]*?\]/g
|
|
508
|
+
];
|
|
509
|
+
|
|
510
|
+
for (const pattern of jsonPatterns) {
|
|
511
|
+
const matches = text.match(pattern);
|
|
512
|
+
if (matches) {
|
|
513
|
+
for (const match of matches) {
|
|
514
|
+
try {
|
|
515
|
+
const parsed = JSON.parse(match.trim());
|
|
516
|
+
// Validate that it looks like an action object
|
|
517
|
+
if (parsed && typeof parsed === 'object' && parsed.type) {
|
|
518
|
+
return parsed;
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
// Continue to next match
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// If still no valid JSON found, try to parse the entire text as JSON
|
|
373
529
|
try {
|
|
374
|
-
|
|
530
|
+
const trimmed = text.trim();
|
|
531
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
532
|
+
return JSON.parse(trimmed);
|
|
533
|
+
}
|
|
375
534
|
} catch (error) {
|
|
376
|
-
|
|
377
|
-
ui.stopThinking();
|
|
378
|
-
return null;
|
|
535
|
+
// Last resort failed
|
|
379
536
|
}
|
|
537
|
+
|
|
538
|
+
console.error('Failed to extract valid JSON from response');
|
|
539
|
+
console.error('Response text was:', text);
|
|
540
|
+
return null;
|
|
380
541
|
}
|
|
381
542
|
|
|
382
543
|
// Call OpenRouter API with tool calling
|
|
@@ -542,10 +703,15 @@ async function executeAction(action) {
|
|
|
542
703
|
}
|
|
543
704
|
throw new Error('Text search is not implemented yet');
|
|
544
705
|
case 'execute':
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
706
|
+
if (data.language === 'bash' || data.language === 'sh') {
|
|
707
|
+
return await agentUtils.runCommand(data.code);
|
|
708
|
+
} else if (data.language === 'node' || data.language === 'javascript' || data.language === 'js') {
|
|
709
|
+
// For node.js code, escape quotes properly and handle multiline code
|
|
710
|
+
const escapedCode = data.code.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
711
|
+
return await agentUtils.runCommand(`node -e "${escapedCode}"`);
|
|
712
|
+
} else {
|
|
713
|
+
throw new Error(`Unsupported execution language: ${data.language}`);
|
|
714
|
+
}
|
|
549
715
|
case 'browse':
|
|
550
716
|
throw new Error('Web browsing is not implemented yet');
|
|
551
717
|
case 'analyze':
|
|
@@ -560,44 +726,77 @@ async function executeAction(action) {
|
|
|
560
726
|
async function processQuery(query, conversation = [], currentModel) {
|
|
561
727
|
try {
|
|
562
728
|
const userMessage = { role: 'user', content: query };
|
|
729
|
+
|
|
730
|
+
// Use existing conversation (which should already have system prompt from chat function)
|
|
563
731
|
let messages = [...conversation, userMessage];
|
|
732
|
+
|
|
564
733
|
let actionCount = 0;
|
|
565
734
|
const MAX_ACTIONS = 10;
|
|
735
|
+
let finalResponse = '';
|
|
566
736
|
|
|
567
737
|
ui.startThinking();
|
|
568
738
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
574
|
-
|
|
575
|
-
if (!actionData || actionData.type === 'stop') {
|
|
576
|
-
ui.stopThinking();
|
|
577
|
-
return {
|
|
578
|
-
response: assistantMessage.content,
|
|
579
|
-
conversation: messages
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
let currentAction = actionData;
|
|
584
|
-
while (currentAction && currentAction.type !== 'stop' && actionCount < MAX_ACTIONS) {
|
|
585
|
-
actionCount++;
|
|
586
|
-
const result = await executeAction(currentAction);
|
|
739
|
+
while (actionCount < MAX_ACTIONS) {
|
|
740
|
+
const responseObj = await callOpenRouter(messages, currentModel, true);
|
|
741
|
+
const assistantMessage = responseObj.choices[0].message;
|
|
742
|
+
messages.push(assistantMessage);
|
|
587
743
|
|
|
588
|
-
|
|
744
|
+
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
589
745
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
746
|
+
// If no valid action found, treat as final response
|
|
747
|
+
if (!actionData || !actionData.type) {
|
|
748
|
+
finalResponse = assistantMessage.content;
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// If stop action, break with the reasoning or content
|
|
753
|
+
if (actionData.type === 'stop') {
|
|
754
|
+
finalResponse = actionData.reasoning || assistantMessage.content;
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
594
757
|
|
|
595
|
-
|
|
758
|
+
actionCount++;
|
|
759
|
+
console.log(`🔧 Executing action ${actionCount}: ${actionData.type}`);
|
|
760
|
+
if (actionData.reasoning) {
|
|
761
|
+
console.log(`📝 Reasoning: ${actionData.reasoning}`);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
try {
|
|
765
|
+
const result = await executeAction(actionData);
|
|
766
|
+
console.log('✅ Action executed successfully');
|
|
767
|
+
|
|
768
|
+
// Add action result to conversation
|
|
769
|
+
const resultMessage = {
|
|
770
|
+
role: 'user',
|
|
771
|
+
content: `Action result (${actionData.type}): ${result}`
|
|
772
|
+
};
|
|
773
|
+
messages.push(resultMessage);
|
|
774
|
+
|
|
775
|
+
} catch (error) {
|
|
776
|
+
console.error('❌ Action execution failed:', error.message);
|
|
777
|
+
|
|
778
|
+
// Add error result to conversation
|
|
779
|
+
const errorMessage = {
|
|
780
|
+
role: 'user',
|
|
781
|
+
content: `Action failed (${actionData.type}): ${error.message}`
|
|
782
|
+
};
|
|
783
|
+
messages.push(errorMessage);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// If we hit max actions, get a final response
|
|
788
|
+
if (actionCount >= MAX_ACTIONS && !finalResponse) {
|
|
789
|
+
const finalMsg = { role: 'user', content: 'Please provide a final summary of what was accomplished.' };
|
|
790
|
+
messages.push(finalMsg);
|
|
791
|
+
|
|
792
|
+
const finalResponseObj = await callOpenRouter(messages, currentModel, true);
|
|
793
|
+
finalResponse = finalResponseObj.choices[0].message.content;
|
|
794
|
+
messages.push(finalResponseObj.choices[0].message);
|
|
596
795
|
}
|
|
597
796
|
|
|
598
797
|
ui.stopThinking();
|
|
599
798
|
return {
|
|
600
|
-
response:
|
|
799
|
+
response: finalResponse || 'Task completed.',
|
|
601
800
|
conversation: messages
|
|
602
801
|
};
|
|
603
802
|
} catch (error) {
|
|
@@ -613,6 +812,14 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
613
812
|
async function chat(rl, useToolCalling, initialModel) {
|
|
614
813
|
let currentModel = initialModel;
|
|
615
814
|
const conversation = [];
|
|
815
|
+
|
|
816
|
+
// Initialize conversation with appropriate system prompt
|
|
817
|
+
if (useToolCalling) {
|
|
818
|
+
conversation.push({ role: 'system', content: TOOL_CALLING_PROMPT });
|
|
819
|
+
} else {
|
|
820
|
+
conversation.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
|
|
821
|
+
}
|
|
822
|
+
|
|
616
823
|
console.log('Type your message, or "exit" to quit.');
|
|
617
824
|
|
|
618
825
|
rl.setPrompt('> ');
|