sam-coder-cli 1.0.0 → 1.0.3

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
@@ -3,13 +3,14 @@
3
3
  const ui = require('./ui.js');
4
4
  const readline = require('readline');
5
5
  const path = require('path');
6
+ const os = require('os');
6
7
  const fs = require('fs').promises;
7
8
  const { exec } = require('child_process');
8
9
  const util = require('util');
9
10
  const execAsync = util.promisify(exec);
10
11
 
11
12
  // Configuration
12
- const CONFIG_PATH = path.join(__dirname, 'config.json');
13
+ const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
13
14
  let OPENROUTER_API_KEY;
14
15
  const MODEL = 'deepseek/deepseek-chat-v3-0324:free';
15
16
  const API_BASE_URL = 'https://openrouter.ai/api/v1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {
Binary file
@@ -1,352 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fetch = require('node-fetch');
4
- const readline = require('readline');
5
- const path = require('path');
6
- const { CLIAgentUtils } = require('../src/cliAgentUtils');
7
-
8
- // Initialize CLIAgentUtils
9
- const agentUtils = new CLIAgentUtils();
10
-
11
- // OpenRouter configuration
12
- const OPENROUTER_API_KEY = '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';
13
- const MODEL = 'deepseek/deepseek-chat-v3-0324:free';
14
- const API_BASE_URL = 'https://openrouter.ai/api/v1';
15
-
16
- // System prompt matching the VS Code extension
17
- const SYSTEM_PROMPT = `You are a VS Code AI Assistant with agency capabilities. You can perform actions on the user's workspace.
18
-
19
- ENVIRONMENT CONTEXT:
20
- - OS: ${process.platform}
21
- - Working Directory: ${process.cwd()}
22
-
23
- When you need to perform actions, respond with JSON in the following format:
24
- \`\`\`json
25
- {
26
- "thoughts": "Your reasoning about what needs to be done",
27
- "actions": [
28
- {
29
- "type": "read|write|search|command|analyze|execute|stop",
30
- "data": { ... action specific data ... }
31
- }
32
- ]
33
- }
34
- \`\`\`
35
-
36
- Action types and their data:
37
- - read: { "path": "relative/or/absolute/path" }
38
- - write: { "path": "relative/or/absolute/path", "content": "file content" }
39
- - search: { "type": "files", "pattern": "glob pattern" } or { "type": "text", "text": "search text" }
40
- - command: { "command": "command string to execute in terminal" }
41
- - execute: { "language": "js|python|bash|...", "code": "code to execute" }
42
- - analyze: { "code": "code to analyze", "question": "what you want to analyze" }
43
- - browse: { "query": "search query", "numResults": 5 } (free web search using DuckDuckGo, optional numResults)
44
- - edit: {
45
- "path": "relative/or/absolute/path",
46
- "edits": {
47
- "operations": [
48
- { "type": "replace", "startLine": 10, "endLine": 15, "newText": "new code here" },
49
- { "type": "replace", "pattern": "oldFunction\\(\\)", "replacement": "newFunction()", "flags": "g" },
50
- { "type": "insert", "line": 20, "text": "new line of code here" },
51
- { "type": "insert", "position": "start", "text": "// Header comment" },
52
- { "type": "insert", "position": "end", "text": "// Footer comment" },
53
- { "type": "delete", "startLine": 25, "endLine": 30 }
54
- ]
55
- }
56
- } (edit specific parts of an existing file)
57
- - stop: {} (use this to indicate you're done with the task and no more actions are needed)
58
-
59
- By default, you will continue to take actions in a loop until you decide to stop with the 'stop' action type.
60
- Always wrap your JSON in markdown code blocks with the json language specifier.
61
- When executing code or commands that might be potentially harmful, explain what the code does before executing it.`;
62
-
63
- async function callOpenRouter(messages, toolCalls = null) {
64
- const body = {
65
- model: MODEL,
66
- messages: messages.map(msg => ({
67
- role: msg.role,
68
- content: msg.content || '',
69
- ...(msg.name && { name: msg.name })
70
- })),
71
- ...(!toolCalls && { tools }),
72
- ...(toolCalls && { tool_calls: toolCalls })
73
- };
74
-
75
- // Remove any undefined values
76
- const cleanBody = JSON.parse(JSON.stringify(body));
77
-
78
- try {
79
- console.log('Sending request to OpenRouter API...');
80
- console.log('Model:', MODEL);
81
- console.log('Headers:', {
82
- 'Content-Type': 'application/json',
83
- 'Authorization': 'Bearer ' + (OPENROUTER_API_KEY ? '***' + OPENROUTER_API_KEY.slice(-4) : 'undefined'),
84
- 'HTTP-Referer': 'https://github.com/yourusername/agi-cli',
85
- 'X-Title': 'AGI-CLI'
86
- });
87
-
88
- console.log('Request body:', JSON.stringify(cleanBody, null, 2));
89
-
90
- const response = await fetch(`${API_BASE_URL}/chat/completions`, {
91
- method: 'POST',
92
- headers: {
93
- 'Content-Type': 'application/json',
94
- 'Authorization': `Bearer ${OPENROUTER_API_KEY}`,
95
- 'HTTP-Referer': 'https://github.com/yourusername/agi-cli',
96
- 'X-Title': 'AGI-CLI'
97
- },
98
- body: JSON.stringify(cleanBody)
99
- });
100
-
101
- console.log('Response status:', response.status, response.statusText);
102
-
103
- if (!response.ok) {
104
- let errorText;
105
- try {
106
- const errorData = await response.text();
107
- console.error('Error response body:', errorData);
108
- const parsedError = JSON.parse(errorData);
109
- errorText = parsedError.error?.message || JSON.stringify(parsedError);
110
- } catch (e) {
111
- errorText = await response.text();
112
- }
113
- throw new Error(`API request failed with status ${response.status}: ${errorText}`);
114
- }
115
-
116
- const responseData = await response.json();
117
- console.log('API Response:', JSON.stringify(responseData, null, 2));
118
- return responseData;
119
- } catch (error) {
120
- console.error('Error in callOpenRouter:', error);
121
- throw new Error(`Failed to call OpenRouter API: ${error.message}`);
122
- }
123
- }
124
-
125
- async function handleToolCalls(toolCalls, messages) {
126
- const results = [];
127
-
128
- for (const toolCall of toolCalls) {
129
- const { name, arguments: args } = toolCall.function;
130
- let result;
131
-
132
- try {
133
- switch (name) {
134
- case 'readFile':
135
- result = await agentUtils.readFile(args.path);
136
- break;
137
- case 'writeFile':
138
- await agentUtils.writeFile(args.path, args.content);
139
-
140
- results.push({
141
- tool_call_id: toolCall.id,
142
- role: 'tool',
143
- name: functionName,
144
- content: JSON.stringify(result)
145
- });
146
- } catch (error) {
147
- console.error('āŒ Tool execution failed:', error);
148
-
149
- results.push({
150
- tool_call_id: toolCall.id,
151
- role: 'tool',
152
- name: functionName,
153
- content: JSON.stringify({ error: error.message })
154
- });
155
- }
156
- }
157
-
158
- return results;
159
- }
160
-
161
- async function extractJsonFromMarkdown(text) {
162
- // Try to find a markdown code block with JSON content
163
- const codeBlockRegex = /```json\n([\s\S]*?)\n```/g;
164
- const match = codeBlockRegex.exec(text);
165
-
166
- if (match) {
167
- try {
168
- return JSON.parse(match[1]);
169
- } catch (error) {
170
- console.error('Error parsing JSON from markdown:', error);
171
- }
172
- }
173
-
174
- // If no code block, try to parse the entire text as JSON
175
- try {
176
- return JSON.parse(text);
177
- } catch (error) {
178
- console.error('Error parsing JSON:', error);
179
- return null;
180
- }
181
- }
182
-
183
- async function processQuery(query, conversation = []) {
184
- try {
185
- // Add user message to conversation
186
- const userMessage = { role: 'user', content: query };
187
- const messages = [...conversation, userMessage];
188
-
189
- // Add system message if this is the first message
190
- if (conversation.length === 0) {
191
- messages.unshift({
192
- role: 'system',
193
- content: SYSTEM_PROMPT
194
- });
195
- }
196
-
197
- let shouldContinue = true;
198
- let iteration = 0;
199
- const maxIterations = 10; // Prevent infinite loops
200
- let finalResponse = '';
201
-
202
- while (shouldContinue && iteration < maxIterations) {
203
- iteration++;
204
- console.log('šŸ¤– Thinking...');
205
-
206
- const response = await callOpenRouter(messages);
207
- const assistantMessage = response.choices[0].message;
208
-
209
- // Add assistant's message to the conversation
210
- messages.push(assistantMessage);
211
-
212
- // Check if the response contains actions
213
- const actionData = await extractJsonFromMarkdown(assistantMessage.content);
214
-
215
- if (actionData && actionData.actions && Array.isArray(actionData.actions)) {
216
- console.log(`šŸ”§ Processing ${actionData.actions.length} actions...`);
217
- if (actionData.thoughts) {
218
- console.log(`šŸ’­ ${actionData.thoughts}`);
219
- }
220
-
221
- const actionResults = [];
222
-
223
- for (const action of actionData.actions) {
224
- console.log(`šŸ› ļø Executing action: ${action.type}`);
225
-
226
- // Handle stop action
227
- if (action.type === 'stop') {
228
- console.log('šŸ›‘ Stop action received, ending action processing');
229
- shouldContinue = false;
230
- finalResponse = 'Task completed successfully.';
231
- break;
232
- }
233
-
234
- try {
235
- let result;
236
-
237
- switch (action.type) {
238
- case 'read':
239
- result = await agentUtils.readFile(action.data.path);
240
- break;
241
-
242
- case 'write':
243
- await agentUtils.writeFile(action.data.path, action.data.content);
244
- result = `Successfully wrote to ${action.data.path}`;
245
- break;
246
-
247
- case 'command':
248
- result = await agentUtils.runCommand(action.data.command);
249
- break;
250
-
251
- case 'search':
252
- if (action.data.type === 'files') {
253
- result = await agentUtils.searchFiles(action.data.pattern);
254
- } else {
255
- result = 'Text search not yet implemented';
256
- }
257
- break;
258
-
259
- default:
260
- result = `Action type '${action.type}' is not supported yet.`;
261
- }
262
-
263
- actionResults.push({
264
- type: action.type,
265
- success: true,
266
- result: result
267
- });
268
-
269
- console.log(`āœ… Action ${action.type} completed successfully`);
270
-
271
- } catch (error) {
272
- console.error(`āŒ Action ${action.type} failed:`, error);
273
- actionResults.push({
274
- type: action.type,
275
- success: false,
276
- error: error.message
277
- });
278
- }
279
- }
280
-
281
- // Add action results to the conversation
282
- messages.push({
283
- role: 'system',
284
- content: `Action results:\n\`\`\`json\n${JSON.stringify(actionResults, null, 2)}\n\`\`\`\n` +
285
- `Based on these results, determine what to do next. You can:\n` +
286
- `1. Continue with more actions by returning a new JSON with "actions" array\n` +
287
- `2. Stop the iteration by including an action with "type": "stop" if the task is completed\n` +
288
- `3. Provide a final response to the user with your findings`
289
- });
290
-
291
- } else {
292
- // No actions, this is a regular response
293
- shouldContinue = false;
294
- finalResponse = assistantMessage.content;
295
- }
296
- }
297
-
298
- // If we hit max iterations, add a note
299
- if (iteration >= maxIterations) {
300
- finalResponse += '\n\nāš ļø Reached maximum number of iterations. Stopping execution.';
301
- }
302
-
303
- return {
304
- response: finalResponse,
305
- conversation: messages
306
- };
307
-
308
- } catch (error) {
309
- console.error('Error processing query:', error);
310
- return {
311
- response: `Error: ${error.message}`,
312
- conversation
313
- };
314
- }
315
- }
316
-
317
- async function chat() {
318
- const conversation = [];
319
- console.log('Welcome to AGI-CLI. Type your message, or "exit" to quit.');
320
-
321
- const rl = readline.createInterface({
322
- input: process.stdin,
323
- output: process.stdout,
324
- prompt: '> '
325
- });
326
-
327
- rl.prompt();
328
-
329
- rl.on('line', async (line) => {
330
- const input = line.trim();
331
-
332
- if (input.toLowerCase() === 'exit') {
333
- rl.close();
334
- return;
335
- }
336
-
337
- if (!input) {
338
- rl.prompt();
339
- return;
340
- }
341
-
342
- const result = await processQuery(input, conversation);
343
- console.log(result.response);
344
- rl.prompt();
345
- }).on('close', () => {
346
- console.log('Goodbye!');
347
- process.exit(0);
348
- });
349
- }
350
-
351
- // Start the chat
352
- chat();