sam-coder-cli 1.0.45 → 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 +262 -158
- package/package.json +1 -1
- package/foldersnake/snake_game.py +0 -218
package/bin/agi-cli.js
CHANGED
|
@@ -15,42 +15,8 @@ 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
|
-
// Tool/Function definitions for the AI
|
|
19
|
-
// Knowledge management functions
|
|
20
|
-
const KNOWLEDGE_FILE = path.join(os.homedir(), '.sam-coder-knowledge.json');
|
|
21
|
-
|
|
22
|
-
async function loadKnowledge() {
|
|
23
|
-
try {
|
|
24
|
-
const data = await fs.readFile(KNOWLEDGE_FILE, 'utf8');
|
|
25
|
-
return JSON.parse(data);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
// If file doesn't exist, return empty knowledge
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function saveKnowledge(knowledge) {
|
|
33
|
-
await fs.writeFile(KNOWLEDGE_FILE, JSON.stringify(knowledge, null, 2), 'utf8');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
18
|
// Tool/Function definitions for the AI
|
|
37
19
|
const tools = [
|
|
38
|
-
{
|
|
39
|
-
type: 'function',
|
|
40
|
-
function: {
|
|
41
|
-
name: 'addKnowledge',
|
|
42
|
-
description: 'Add or update knowledge in the knowledge base',
|
|
43
|
-
parameters: {
|
|
44
|
-
type: 'object',
|
|
45
|
-
properties: {
|
|
46
|
-
key: { type: 'string', description: 'Key/identifier for the knowledge' },
|
|
47
|
-
value: { type: 'string', description: 'The knowledge content' },
|
|
48
|
-
description: { type: 'string', description: 'Description of what this knowledge is about' }
|
|
49
|
-
},
|
|
50
|
-
required: ['key', 'value', 'description']
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
20
|
{
|
|
55
21
|
type: 'function',
|
|
56
22
|
function: {
|
|
@@ -224,19 +190,12 @@ const tools = [
|
|
|
224
190
|
// System prompt for the AI Assistant when using tool calling
|
|
225
191
|
const TOOL_CALLING_PROMPT = `You are a helpful AI assistant with agency capabilities. You can perform actions on the user's system using the provided tools.
|
|
226
192
|
|
|
227
|
-
KNOWLEDGE MANAGEMENT:
|
|
228
|
-
- You can store and retrieve knowledge using the 'addKnowledge' tool.
|
|
229
|
-
- Use this to remember user preferences, project details, or any other important information.
|
|
230
|
-
- The knowledge will persist across sessions.
|
|
231
|
-
- Example: If the user mentions they prefer Python for all projects, store this preference.
|
|
232
|
-
|
|
233
193
|
TOOLS AVAILABLE:
|
|
234
|
-
1.
|
|
235
|
-
2.
|
|
236
|
-
3.
|
|
237
|
-
4.
|
|
238
|
-
5.
|
|
239
|
-
6. searchFiles - Search for files using a glob pattern
|
|
194
|
+
1. readFile - Read the contents of a file
|
|
195
|
+
2. writeFile - Write content to a file
|
|
196
|
+
3. editFile - Edit specific parts of a file
|
|
197
|
+
4. runCommand - Execute a shell command
|
|
198
|
+
5. searchFiles - Search for files using a glob pattern
|
|
240
199
|
|
|
241
200
|
ENVIRONMENT:
|
|
242
201
|
- OS: ${process.platform}
|
|
@@ -252,11 +211,130 @@ INSTRUCTIONS:
|
|
|
252
211
|
Always think step by step and explain your reasoning before taking actions that could affect the system.`;
|
|
253
212
|
|
|
254
213
|
// System prompt for the AI Assistant when using legacy function calling (JSON actions)
|
|
255
|
-
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
|
+
}
|
|
256
234
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
+
\`\`\`
|
|
324
|
+
|
|
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.`;
|
|
260
338
|
|
|
261
339
|
// Agent utilities
|
|
262
340
|
const agentUtils = {
|
|
@@ -396,50 +474,70 @@ const agentUtils = {
|
|
|
396
474
|
}
|
|
397
475
|
};
|
|
398
476
|
|
|
399
|
-
const toolHandlers = {
|
|
400
|
-
addKnowledge: async ({ key, value, description }) => {
|
|
401
|
-
try {
|
|
402
|
-
const knowledge = await loadKnowledge();
|
|
403
|
-
knowledge[key] = {
|
|
404
|
-
value,
|
|
405
|
-
description,
|
|
406
|
-
timestamp: new Date().toISOString()
|
|
407
|
-
};
|
|
408
|
-
await saveKnowledge(knowledge);
|
|
409
|
-
return { success: true, message: `Knowledge '${key}' added successfully` };
|
|
410
|
-
} catch (error) {
|
|
411
|
-
return { success: false, error: error.message };
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
readFile,
|
|
415
|
-
writeFile,
|
|
416
|
-
editFile,
|
|
417
|
-
runCommand,
|
|
418
|
-
searchFiles
|
|
419
|
-
};
|
|
420
|
-
|
|
421
477
|
// Extract JSON from markdown code blocks
|
|
422
478
|
function extractJsonFromMarkdown(text) {
|
|
423
|
-
|
|
424
|
-
|
|
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;
|
|
425
486
|
const match = text.match(codeBlockRegex);
|
|
426
487
|
|
|
427
|
-
if (match) {
|
|
488
|
+
if (match && match[1]) {
|
|
428
489
|
try {
|
|
429
|
-
|
|
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);
|
|
430
496
|
} catch (error) {
|
|
431
|
-
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]);
|
|
432
499
|
}
|
|
433
500
|
}
|
|
434
501
|
|
|
435
|
-
// 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
|
|
436
529
|
try {
|
|
437
|
-
|
|
530
|
+
const trimmed = text.trim();
|
|
531
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
532
|
+
return JSON.parse(trimmed);
|
|
533
|
+
}
|
|
438
534
|
} catch (error) {
|
|
439
|
-
|
|
440
|
-
ui.stopThinking();
|
|
441
|
-
return null;
|
|
535
|
+
// Last resort failed
|
|
442
536
|
}
|
|
537
|
+
|
|
538
|
+
console.error('Failed to extract valid JSON from response');
|
|
539
|
+
console.error('Response text was:', text);
|
|
540
|
+
return null;
|
|
443
541
|
}
|
|
444
542
|
|
|
445
543
|
// Call OpenRouter API with tool calling
|
|
@@ -491,29 +589,7 @@ async function callOpenRouter(messages, currentModel, useJson = false) {
|
|
|
491
589
|
// Process a query with tool calling
|
|
492
590
|
async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
493
591
|
const userMessage = { role: 'user', content: query };
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
// Add system message with knowledge if this is the first message
|
|
497
|
-
if (conversation.length === 0) {
|
|
498
|
-
// Load knowledge and include it in the system prompt
|
|
499
|
-
const knowledge = await loadKnowledge();
|
|
500
|
-
let systemPrompt = TOOL_CALLING_PROMPT;
|
|
501
|
-
|
|
502
|
-
// Add knowledge to the system prompt if any exists
|
|
503
|
-
const knowledgeEntries = Object.entries(knowledge);
|
|
504
|
-
if (knowledgeEntries.length > 0) {
|
|
505
|
-
systemPrompt += '\n\nCURRENT KNOWLEDGE BASE:\n';
|
|
506
|
-
systemPrompt += knowledgeEntries.map(([key, { value, description }]) =>
|
|
507
|
-
`- ${key}: ${value} (${description})`
|
|
508
|
-
).join('\n');
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Add system message at the beginning
|
|
512
|
-
messages = [
|
|
513
|
-
{ role: 'system', content: systemPrompt },
|
|
514
|
-
...messages
|
|
515
|
-
];
|
|
516
|
-
}
|
|
592
|
+
const messages = [...conversation, userMessage];
|
|
517
593
|
|
|
518
594
|
ui.startThinking();
|
|
519
595
|
|
|
@@ -556,20 +632,12 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
556
632
|
async function handleToolCalls(toolCalls, messages) {
|
|
557
633
|
const results = [];
|
|
558
634
|
|
|
559
|
-
// Load knowledge at the start of processing tool calls
|
|
560
|
-
const knowledge = await loadKnowledge();
|
|
561
|
-
|
|
562
635
|
for (const toolCall of toolCalls) {
|
|
563
636
|
const functionName = toolCall.function.name;
|
|
564
637
|
let args;
|
|
565
638
|
|
|
566
639
|
try {
|
|
567
640
|
args = JSON.parse(toolCall.function.arguments);
|
|
568
|
-
|
|
569
|
-
// If we have knowledge that might be relevant, include it in the context
|
|
570
|
-
if (knowledge.defaultLanguage && !args.language) {
|
|
571
|
-
args.language = knowledge.defaultLanguage.value;
|
|
572
|
-
}
|
|
573
641
|
} catch (error) {
|
|
574
642
|
console.error('❌ Failed to parse tool arguments:', error);
|
|
575
643
|
results.push({
|
|
@@ -635,10 +703,15 @@ async function executeAction(action) {
|
|
|
635
703
|
}
|
|
636
704
|
throw new Error('Text search is not implemented yet');
|
|
637
705
|
case 'execute':
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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
|
+
}
|
|
642
715
|
case 'browse':
|
|
643
716
|
throw new Error('Web browsing is not implemented yet');
|
|
644
717
|
case 'analyze':
|
|
@@ -653,44 +726,77 @@ async function executeAction(action) {
|
|
|
653
726
|
async function processQuery(query, conversation = [], currentModel) {
|
|
654
727
|
try {
|
|
655
728
|
const userMessage = { role: 'user', content: query };
|
|
729
|
+
|
|
730
|
+
// Use existing conversation (which should already have system prompt from chat function)
|
|
656
731
|
let messages = [...conversation, userMessage];
|
|
732
|
+
|
|
657
733
|
let actionCount = 0;
|
|
658
734
|
const MAX_ACTIONS = 10;
|
|
735
|
+
let finalResponse = '';
|
|
659
736
|
|
|
660
737
|
ui.startThinking();
|
|
661
738
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
667
|
-
|
|
668
|
-
if (!actionData || actionData.type === 'stop') {
|
|
669
|
-
ui.stopThinking();
|
|
670
|
-
return {
|
|
671
|
-
response: assistantMessage.content,
|
|
672
|
-
conversation: messages
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
let currentAction = actionData;
|
|
677
|
-
while (currentAction && currentAction.type !== 'stop' && actionCount < MAX_ACTIONS) {
|
|
678
|
-
actionCount++;
|
|
679
|
-
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);
|
|
680
743
|
|
|
681
|
-
|
|
744
|
+
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
682
745
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
+
}
|
|
757
|
+
|
|
758
|
+
actionCount++;
|
|
759
|
+
console.log(`🔧 Executing action ${actionCount}: ${actionData.type}`);
|
|
760
|
+
if (actionData.reasoning) {
|
|
761
|
+
console.log(`📝 Reasoning: ${actionData.reasoning}`);
|
|
762
|
+
}
|
|
687
763
|
|
|
688
|
-
|
|
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);
|
|
689
795
|
}
|
|
690
796
|
|
|
691
797
|
ui.stopThinking();
|
|
692
798
|
return {
|
|
693
|
-
response:
|
|
799
|
+
response: finalResponse || 'Task completed.',
|
|
694
800
|
conversation: messages
|
|
695
801
|
};
|
|
696
802
|
} catch (error) {
|
|
@@ -706,6 +812,14 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
706
812
|
async function chat(rl, useToolCalling, initialModel) {
|
|
707
813
|
let currentModel = initialModel;
|
|
708
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
|
+
|
|
709
823
|
console.log('Type your message, or "exit" to quit.');
|
|
710
824
|
|
|
711
825
|
rl.setPrompt('> ');
|
|
@@ -740,28 +854,18 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
740
854
|
if (input.toLowerCase() === '/setup') {
|
|
741
855
|
await runSetup(rl, true);
|
|
742
856
|
console.log('\nSetup complete. Please restart the application to apply changes.');
|
|
743
|
-
rl.
|
|
857
|
+
rl.close();
|
|
744
858
|
return;
|
|
745
859
|
}
|
|
746
|
-
|
|
747
|
-
if (input.toLowerCase() === '
|
|
748
|
-
|
|
749
|
-
await fs.unlink(KNOWLEDGE_FILE);
|
|
750
|
-
console.log('✅ All knowledge has been erased.');
|
|
751
|
-
} catch (error) {
|
|
752
|
-
if (error.code === 'ENOENT') {
|
|
753
|
-
console.log('ℹ️ No knowledge to erase.');
|
|
754
|
-
} else {
|
|
755
|
-
console.error('❌ Failed to erase knowledge:', error.message);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
rl.prompt();
|
|
860
|
+
|
|
861
|
+
if (input.toLowerCase() === 'exit') {
|
|
862
|
+
rl.close();
|
|
759
863
|
return;
|
|
760
864
|
}
|
|
761
865
|
|
|
762
866
|
const result = useToolCalling
|
|
763
|
-
? await processQueryWithTools(
|
|
764
|
-
: await processQuery(
|
|
867
|
+
? await processQueryWithTools(input, conversation, currentModel)
|
|
868
|
+
: await processQuery(input, conversation, currentModel);
|
|
765
869
|
ui.stopThinking();
|
|
766
870
|
ui.showResponse(result.response);
|
|
767
871
|
|
package/package.json
CHANGED
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import pygame
|
|
2
|
-
import random
|
|
3
|
-
|
|
4
|
-
# Initialize pygame
|
|
5
|
-
pygame.init()
|
|
6
|
-
|
|
7
|
-
# Constants
|
|
8
|
-
SCREEN_WIDTH = 300
|
|
9
|
-
SCREEN_HEIGHT = 600
|
|
10
|
-
BLOCK_SIZE = 30
|
|
11
|
-
GRID_WIDTH = 10
|
|
12
|
-
GRID_HEIGHT = 20
|
|
13
|
-
GAME_AREA_LEFT = (SCREEN_WIDTH - GRID_WIDTH * BLOCK_SIZE) // 2
|
|
14
|
-
GAME_AREA_TOP = SCREEN_HEIGHT - GRID_HEIGHT * BLOCK_SIZE
|
|
15
|
-
|
|
16
|
-
# Colors
|
|
17
|
-
BLACK = (0, 0, 0)
|
|
18
|
-
WHITE = (255, 255, 255)
|
|
19
|
-
GRAY = (128, 128, 128)
|
|
20
|
-
COLORS = [
|
|
21
|
-
(0, 255, 255), # Cyan (I)
|
|
22
|
-
(0, 0, 255), # Blue (J)
|
|
23
|
-
(255, 165, 0), # Orange (L)
|
|
24
|
-
(255, 255, 0), # Yellow (O)
|
|
25
|
-
(0, 255, 0), # Green (S)
|
|
26
|
-
(128, 0, 128), # Purple (T)
|
|
27
|
-
(255, 0, 0) # Red (Z)
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
# Tetrimino shapes
|
|
31
|
-
SHAPES = [
|
|
32
|
-
[[1, 1, 1, 1]], # I
|
|
33
|
-
[[1, 0, 0], [1, 1, 1]], # J
|
|
34
|
-
[[0, 0, 1], [1, 1, 1]], # L
|
|
35
|
-
[[1, 1], [1, 1]], # O
|
|
36
|
-
[[0, 1, 1], [1, 1, 0]], # S
|
|
37
|
-
[[0, 1, 0], [1, 1, 1]], # T
|
|
38
|
-
[[1, 1, 0], [0, 1, 1]] # Z
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
# Set up the display
|
|
42
|
-
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
|
43
|
-
pygame.display.set_caption("Tetris")
|
|
44
|
-
|
|
45
|
-
clock = pygame.time.Clock()
|
|
46
|
-
|
|
47
|
-
# Game variables
|
|
48
|
-
grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
|
|
49
|
-
current_piece = None
|
|
50
|
-
next_piece = None
|
|
51
|
-
current_x = GRID_WIDTH // 2
|
|
52
|
-
current_y = 0
|
|
53
|
-
score = 0
|
|
54
|
-
game_over = False
|
|
55
|
-
fall_time = 0
|
|
56
|
-
fall_speed = 0.5 # seconds
|
|
57
|
-
|
|
58
|
-
def new_piece():
|
|
59
|
-
global current_piece, next_piece, current_x, current_y
|
|
60
|
-
if next_piece is None:
|
|
61
|
-
next_piece = random.randint(0, len(SHAPES) - 1)
|
|
62
|
-
current_piece = next_piece
|
|
63
|
-
next_piece = random.randint(0, len(SHAPES) - 1)
|
|
64
|
-
current_x = GRID_WIDTH // 2 - len(SHAPES[current_piece][0]) // 2
|
|
65
|
-
current_y = 0
|
|
66
|
-
if check_collision():
|
|
67
|
-
return False
|
|
68
|
-
return True
|
|
69
|
-
|
|
70
|
-
def check_collision():
|
|
71
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
72
|
-
for x, cell in enumerate(row):
|
|
73
|
-
if cell:
|
|
74
|
-
if (current_y + y >= GRID_HEIGHT or
|
|
75
|
-
current_x + x < 0 or
|
|
76
|
-
current_x + x >= GRID_WIDTH or
|
|
77
|
-
grid[current_y + y][current_x + x]):
|
|
78
|
-
return True
|
|
79
|
-
return False
|
|
80
|
-
|
|
81
|
-
def merge_piece():
|
|
82
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
83
|
-
for x, cell in enumerate(row):
|
|
84
|
-
if cell:
|
|
85
|
-
grid[current_y + y][current_x + x] = current_piece + 1
|
|
86
|
-
|
|
87
|
-
def clear_lines():
|
|
88
|
-
global grid, score
|
|
89
|
-
lines_cleared = 0
|
|
90
|
-
for y in range(GRID_HEIGHT - 1, -1, -1):
|
|
91
|
-
if all(grid[y]):
|
|
92
|
-
lines_cleared += 1
|
|
93
|
-
for y2 in range(y, 0, -1):
|
|
94
|
-
grid[y2] = grid[y2 - 1][:]
|
|
95
|
-
grid[0] = [0] * GRID_WIDTH
|
|
96
|
-
score += lines_cleared ** 2 * 100
|
|
97
|
-
|
|
98
|
-
def draw_grid():
|
|
99
|
-
for y in range(GRID_HEIGHT):
|
|
100
|
-
for x in range(GRID_WIDTH):
|
|
101
|
-
if grid[y][x]:
|
|
102
|
-
pygame.draw.rect(
|
|
103
|
-
screen, COLORS[grid[y][x] - 1],
|
|
104
|
-
(GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
105
|
-
)
|
|
106
|
-
pygame.draw.rect(
|
|
107
|
-
screen, WHITE,
|
|
108
|
-
(GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
109
|
-
1
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
def draw_piece():
|
|
113
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
114
|
-
for x, cell in enumerate(row):
|
|
115
|
-
if cell:
|
|
116
|
-
pygame.draw.rect(
|
|
117
|
-
screen, COLORS[current_piece],
|
|
118
|
-
(GAME_AREA_LEFT + (current_x + x) * BLOCK_SIZE, GAME_AREA_TOP + (current_y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
119
|
-
)
|
|
120
|
-
pygame.draw.rect(
|
|
121
|
-
screen, WHITE,
|
|
122
|
-
(GAME_AREA_LEFT + (current_x + x) * BLOCK_SIZE, GAME_AREA_TOP + (current_y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
123
|
-
1
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
def draw_next_piece():
|
|
127
|
-
if next_piece is not None:
|
|
128
|
-
font = pygame.font.SysFont(None, 24)
|
|
129
|
-
text = font.render("Next:", True, WHITE)
|
|
130
|
-
screen.blit(text, (20, 20))
|
|
131
|
-
for y, row in enumerate(SHAPES[next_piece]):
|
|
132
|
-
for x, cell in enumerate(row):
|
|
133
|
-
if cell:
|
|
134
|
-
pygame.draw.rect(
|
|
135
|
-
screen, COLORS[next_piece],
|
|
136
|
-
(40 + x * BLOCK_SIZE, 50 + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
137
|
-
)
|
|
138
|
-
pygame.draw.rect(
|
|
139
|
-
screen, WHITE,
|
|
140
|
-
(40 + x * BLOCK_SIZE, 50 + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
141
|
-
1
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
def draw_score():
|
|
145
|
-
font = pygame.font.SysFont(None, 36)
|
|
146
|
-
text = font.render(f"Score: {score}", True, WHITE)
|
|
147
|
-
screen.blit(text, (20, SCREEN_HEIGHT - 50))
|
|
148
|
-
|
|
149
|
-
def rotate_piece():
|
|
150
|
-
global current_piece
|
|
151
|
-
rotated = list(zip(*reversed(SHAPES[current_piece])))
|
|
152
|
-
for y in range(len(rotated)):
|
|
153
|
-
rotated[y] = list(rotated[y])
|
|
154
|
-
old_shape = SHAPES[current_piece]
|
|
155
|
-
SHAPES[current_piece] = rotated
|
|
156
|
-
if check_collision():
|
|
157
|
-
SHAPES[current_piece] = old_shape
|
|
158
|
-
|
|
159
|
-
# Main game loop
|
|
160
|
-
new_piece()
|
|
161
|
-
running = True
|
|
162
|
-
while running:
|
|
163
|
-
screen.fill(BLACK)
|
|
164
|
-
fall_time += clock.get_rawtime() / 1000 # Convert to seconds
|
|
165
|
-
clock.tick()
|
|
166
|
-
|
|
167
|
-
if fall_time >= fall_speed:
|
|
168
|
-
fall_time = 0
|
|
169
|
-
current_y += 1
|
|
170
|
-
if check_collision():
|
|
171
|
-
current_y -= 1
|
|
172
|
-
merge_piece()
|
|
173
|
-
clear_lines()
|
|
174
|
-
if not new_piece():
|
|
175
|
-
game_over = True
|
|
176
|
-
|
|
177
|
-
for event in pygame.event.get():
|
|
178
|
-
if event.type == pygame.QUIT:
|
|
179
|
-
running = False
|
|
180
|
-
if event.type == pygame.KEYDOWN:
|
|
181
|
-
if event.key == pygame.K_LEFT:
|
|
182
|
-
current_x -= 1
|
|
183
|
-
if check_collision():
|
|
184
|
-
current_x += 1
|
|
185
|
-
if event.key == pygame.K_RIGHT:
|
|
186
|
-
current_x += 1
|
|
187
|
-
if check_collision():
|
|
188
|
-
current_x -= 1
|
|
189
|
-
if event.key == pygame.K_DOWN:
|
|
190
|
-
current_y += 1
|
|
191
|
-
if check_collision():
|
|
192
|
-
current_y -= 1
|
|
193
|
-
if event.key == pygame.K_UP:
|
|
194
|
-
rotate_piece()
|
|
195
|
-
if event.key == pygame.K_SPACE:
|
|
196
|
-
while not check_collision():
|
|
197
|
-
current_y += 1
|
|
198
|
-
current_y -= 1
|
|
199
|
-
merge_piece()
|
|
200
|
-
clear_lines()
|
|
201
|
-
if not new_piece():
|
|
202
|
-
game_over = True
|
|
203
|
-
|
|
204
|
-
draw_grid()
|
|
205
|
-
draw_piece()
|
|
206
|
-
draw_next_piece()
|
|
207
|
-
draw_score()
|
|
208
|
-
pygame.display.update()
|
|
209
|
-
|
|
210
|
-
if game_over:
|
|
211
|
-
font = pygame.font.SysFont(None, 48)
|
|
212
|
-
text = font.render("GAME OVER", True, WHITE)
|
|
213
|
-
screen.blit(text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 24))
|
|
214
|
-
pygame.display.update()
|
|
215
|
-
pygame.time.wait(2000)
|
|
216
|
-
running = False
|
|
217
|
-
|
|
218
|
-
pygame.quit()
|