sam-coder-cli 1.0.33 ā 1.0.35
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/ai-team.js +197 -0
- package/bin/multiplayer-client.js +29 -4
- package/bin/multiplayer-mode.js +25 -83
- package/package.json +5 -2
package/bin/ai-team.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const { callOpenRouter } = require('./agi-cli');
|
|
2
|
+
const { MultiplayerClient } = require('./multiplayer-client');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const { v4: uuidv4 } = require('uuid');
|
|
6
|
+
|
|
7
|
+
// System prompt for AI team collaboration
|
|
8
|
+
const TEAM_COLLABORATION_PROMPT = `You are an AI team facilitator that helps coordinate multiple AI agents working together on a project.
|
|
9
|
+
|
|
10
|
+
Your responsibilities include:
|
|
11
|
+
1. Understanding the project requirements from the host
|
|
12
|
+
2. Breaking down the project into clear, actionable tasks
|
|
13
|
+
3. Assigning tasks to appropriate team members based on their roles
|
|
14
|
+
4. Monitoring progress and coordinating between team members
|
|
15
|
+
|
|
16
|
+
When the host provides a project idea (e.g., "create a calculator"), you should:
|
|
17
|
+
1. Analyze the requirements
|
|
18
|
+
2. Break it down into logical components
|
|
19
|
+
3. Create a task list with clear assignments
|
|
20
|
+
4. Provide a summary of the plan
|
|
21
|
+
|
|
22
|
+
Respond in markdown format with clear sections for each part of your response.`;
|
|
23
|
+
|
|
24
|
+
class AITeam {
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.client = new MultiplayerClient({
|
|
27
|
+
name: options.name || `Agent-${uuidv4().substr(0, 4)}`,
|
|
28
|
+
role: options.role || 'DEVELOPER',
|
|
29
|
+
model: options.model || 'deepseek/deepseek-chat-v3-0324:free',
|
|
30
|
+
rl: options.rl
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.rl = options.rl || readline.createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: process.stdout
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.conversation = [
|
|
39
|
+
{ role: 'system', content: TEAM_COLLABORATION_PROMPT }
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
this.setupEventHandlers();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setupEventHandlers() {
|
|
46
|
+
this.client.on('connected', () => {
|
|
47
|
+
console.log(chalk.green('ā
Connected to multiplayer server'));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.client.on('disconnected', () => {
|
|
51
|
+
console.log(chalk.yellow('Disconnected from multiplayer server'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.client.on('error', (error) => {
|
|
55
|
+
console.error(chalk.red('Error:'), error.message);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this.client.on('session_joined', async (data) => {
|
|
59
|
+
console.log(chalk.green(`\nš Joined session ${data.sessionId} as ${this.client.name}`));
|
|
60
|
+
console.log(chalk.blue(`Role: ${this.client.role}`));
|
|
61
|
+
|
|
62
|
+
if (data.isHost) {
|
|
63
|
+
console.log('\nAs the host, you can now enter a project prompt (e.g., "create a calculator"):');
|
|
64
|
+
await this.promptForProject();
|
|
65
|
+
} else {
|
|
66
|
+
console.log('\nWaiting for host to start a project...');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.client.on('chat_message', async (message) => {
|
|
71
|
+
if (message.clientId !== this.client.clientId) {
|
|
72
|
+
console.log(`\nš¬ ${message.clientName || 'Unknown'}: ${message.text}`);
|
|
73
|
+
|
|
74
|
+
// If we receive a task assignment, process it
|
|
75
|
+
if (message.task) {
|
|
76
|
+
await this.processTask(message.task);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async start(sessionId = null) {
|
|
83
|
+
let serverId = null;
|
|
84
|
+
let actualSessionId = sessionId;
|
|
85
|
+
|
|
86
|
+
// Check if sessionId contains server information (format: serverId:sessionId)
|
|
87
|
+
if (sessionId && sessionId.includes(':')) {
|
|
88
|
+
[serverId, actualSessionId] = sessionId.split(':');
|
|
89
|
+
console.log(chalk.blue(`Attempting to connect to server: ${serverId}`));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Connect to the server first
|
|
93
|
+
try {
|
|
94
|
+
await this.client.connect(serverId);
|
|
95
|
+
|
|
96
|
+
if (actualSessionId) {
|
|
97
|
+
console.log(`Joining session ${actualSessionId}...`);
|
|
98
|
+
await this.client.joinSession(actualSessionId);
|
|
99
|
+
} else {
|
|
100
|
+
console.log('Creating new session...');
|
|
101
|
+
const newSessionId = await this.client.createSession();
|
|
102
|
+
console.log(chalk.green(`ā
Created new session: ${newSessionId}`));
|
|
103
|
+
console.log('Share this ID with others to collaborate!');
|
|
104
|
+
console.log(chalk.yellow(`To join this session, use: ${serverId || 'localhost:8080'}:${newSessionId}`));
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(chalk.red('Failed to connect to server:'), error.message);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Start chat input loop
|
|
112
|
+
this.rl.on('line', async (input) => {
|
|
113
|
+
if (input.trim()) {
|
|
114
|
+
if (this.client.isHost) {
|
|
115
|
+
await this.handleHostInput(input.trim());
|
|
116
|
+
} else {
|
|
117
|
+
await this.handleTeamMemberInput(input.trim());
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async handleHostInput(input) {
|
|
124
|
+
// Add user message to conversation
|
|
125
|
+
this.conversation.push({ role: 'user', content: input });
|
|
126
|
+
|
|
127
|
+
// Get AI response
|
|
128
|
+
const response = await callOpenRouter(this.conversation, this.client.model);
|
|
129
|
+
const aiMessage = response.choices[0].message;
|
|
130
|
+
|
|
131
|
+
// Add AI response to conversation
|
|
132
|
+
this.conversation.push(aiMessage);
|
|
133
|
+
|
|
134
|
+
// Display response
|
|
135
|
+
console.log(`\nš¤ ${aiMessage.content}\n`);
|
|
136
|
+
|
|
137
|
+
// If AI suggests tasks, assign them to team members
|
|
138
|
+
if (aiMessage.tool_calls) {
|
|
139
|
+
await this.handleToolCalls(aiMessage.tool_calls);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Show prompt for next input
|
|
143
|
+
this.showPrompt();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async handleTeamMemberInput(input) {
|
|
147
|
+
// For team members, just send chat messages for now
|
|
148
|
+
this.client.sendChatMessage(input);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async handleToolCalls(toolCalls) {
|
|
152
|
+
for (const toolCall of toolCalls) {
|
|
153
|
+
if (toolCall.function.name === 'assign_task') {
|
|
154
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
155
|
+
await this.assignTask(args.role, args.task);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async assignTask(role, task) {
|
|
161
|
+
console.log(chalk.yellow(`\nš Task for ${role}: ${task}`));
|
|
162
|
+
|
|
163
|
+
// In a real implementation, this would find the appropriate team member
|
|
164
|
+
// and assign them the task. For now, we'll just log it.
|
|
165
|
+
this.client.sendChatMessage(`Task assigned to ${role}: ${task}`, {
|
|
166
|
+
task: { role, description: task }
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async processTask(task) {
|
|
171
|
+
console.log(chalk.blue(`\nš Task received: ${task.description}`));
|
|
172
|
+
|
|
173
|
+
// Process the task using the AGI-CLI's tool calling system
|
|
174
|
+
const response = await callOpenRouter([
|
|
175
|
+
{ role: 'system', content: `You are an AI assistant working on a task: ${task.description}` },
|
|
176
|
+
{ role: 'user', content: 'Please complete this task step by step.' }
|
|
177
|
+
], this.client.model, true);
|
|
178
|
+
|
|
179
|
+
const result = response.choices[0].message.content;
|
|
180
|
+
console.log(chalk.green('ā
Task completed!'));
|
|
181
|
+
console.log(result);
|
|
182
|
+
|
|
183
|
+
// Notify the team of task completion
|
|
184
|
+
this.client.sendChatMessage(`Completed task: ${task.description}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
showPrompt() {
|
|
188
|
+
const prefix = this.client.isHost ? 'š Host' : 'š¤ You';
|
|
189
|
+
process.stdout.write(`\n${prefix} > `);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async promptForProject() {
|
|
193
|
+
this.showPrompt();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = AITeam;
|
|
@@ -34,7 +34,32 @@ async function findAvailablePort(startPort = 8080, maxAttempts = 10) {
|
|
|
34
34
|
throw new Error(`Could not find an available port after ${maxAttempts} attempts`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
async function ensureServerRunning(port = 8080) {
|
|
37
|
+
async function ensureServerRunning(port = 8080, serverId = null) {
|
|
38
|
+
// First try to connect to an existing server by ID if provided
|
|
39
|
+
if (serverId) {
|
|
40
|
+
try {
|
|
41
|
+
console.log(chalk.blue(`Attempting to connect to existing server ${serverId}...`));
|
|
42
|
+
// Try to connect to the server
|
|
43
|
+
const ws = new WebSocket(`ws://${serverId}`);
|
|
44
|
+
|
|
45
|
+
// Wait for connection or timeout
|
|
46
|
+
const connectionResult = await Promise.race([
|
|
47
|
+
new Promise(resolve => ws.once('open', () => resolve(true))),
|
|
48
|
+
new Promise(resolve => setTimeout(() => resolve(false), 2000))
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
if (connectionResult) {
|
|
52
|
+
console.log(chalk.green(`ā
Connected to existing server at ${serverId}`));
|
|
53
|
+
ws.terminate();
|
|
54
|
+
return `ws://${serverId}`;
|
|
55
|
+
}
|
|
56
|
+
ws.terminate();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.log(chalk.yellow(`Could not connect to server ${serverId}, starting a new server...`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// If no server ID provided or connection failed, start a new server
|
|
38
63
|
try {
|
|
39
64
|
const availablePort = await findAvailablePort(port);
|
|
40
65
|
const isDefaultPort = (availablePort === port);
|
|
@@ -271,14 +296,14 @@ class MultiplayerClient extends EventEmitter {
|
|
|
271
296
|
this.completeTask = this.completeTask.bind(this);
|
|
272
297
|
}
|
|
273
298
|
|
|
274
|
-
async connect() {
|
|
299
|
+
async connect(serverId = null) {
|
|
275
300
|
return new Promise((resolve, reject) => {
|
|
276
301
|
const connectWithRetry = async () => {
|
|
277
302
|
try {
|
|
278
|
-
// If no server URL is provided, try to start a local server
|
|
303
|
+
// If no server URL is provided, try to start a local server or connect to the specified server ID
|
|
279
304
|
if (this.serverUrl === 'ws://localhost:8080') {
|
|
280
305
|
try {
|
|
281
|
-
this.serverUrl = await ensureServerRunning(8080);
|
|
306
|
+
this.serverUrl = await ensureServerRunning(8080, serverId);
|
|
282
307
|
} catch (error) {
|
|
283
308
|
console.error(chalk.red('Failed to start local server:'), error.message);
|
|
284
309
|
reject(error);
|
package/bin/multiplayer-mode.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
const
|
|
1
|
+
const AITeam = require('./ai-team');
|
|
2
2
|
const readline = require('readline');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const { v4: uuidv4 } = require('uuid');
|
|
5
4
|
const ui = require('./ui');
|
|
6
5
|
|
|
7
6
|
class MultiplayerMode {
|
|
@@ -11,15 +10,16 @@ class MultiplayerMode {
|
|
|
11
10
|
output: process.stdout
|
|
12
11
|
});
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
// Create AI Team instance
|
|
14
|
+
this.aiTeam = new AITeam({
|
|
15
|
+
name: options.agentName || this.generateAgentName(),
|
|
16
|
+
role: options.role || 'DEVELOPER',
|
|
17
|
+
model: options.model || 'deepseek/deepseek-chat-v3-0324:free',
|
|
15
18
|
rl: this.rl,
|
|
16
|
-
agentName: options.agentName || this.generateAgentName(),
|
|
17
|
-
model: options.model || 'default',
|
|
18
19
|
serverUrl: options.serverUrl
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
this.
|
|
22
|
-
this.agentRole = '';
|
|
22
|
+
this.agentRole = options.role || 'DEVELOPER';
|
|
23
23
|
this.isWorking = false;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -32,89 +32,31 @@ class MultiplayerMode {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
setupEventListeners() {
|
|
35
|
-
|
|
36
|
-
console.log(chalk.green('Connected to multiplayer server'));
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
this.client.on('disconnected', () => {
|
|
40
|
-
console.log(chalk.yellow('Disconnected from multiplayer server'));
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
this.client.on('session_joined', (data) => {
|
|
44
|
-
ui.showHeader();
|
|
45
|
-
console.log(chalk.green(`\nJoined session: ${data.sessionId}`));
|
|
46
|
-
console.log(chalk.blue(`You are: ${this.client.agentName}`));
|
|
47
|
-
console.log(chalk.blue(`Model: ${this.client.model}`));
|
|
48
|
-
console.log(chalk.blue(`Clients in session: ${Array.from(this.client.clients.values()).map(c => c.name).join(', ')}`));
|
|
49
|
-
|
|
50
|
-
if (this.client.isHost) {
|
|
51
|
-
console.log(chalk.yellow('\nYou are the host. Type /help for available commands.'));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.prompt();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
this.client.on('agent_joined', (data) => {
|
|
58
|
-
console.log(chalk.blue(`\n${data.clientInfo.name} joined the session`));
|
|
59
|
-
this.prompt();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
this.client.on('agent_left', (data) => {
|
|
63
|
-
const client = this.client.clients.get(data.clientId);
|
|
64
|
-
if (client) {
|
|
65
|
-
console.log(chalk.yellow(`\n${client.name} left the session`));
|
|
66
|
-
this.prompt();
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
this.client.on('chat', (message) => {
|
|
71
|
-
// Chat messages are already handled by the client
|
|
72
|
-
this.prompt();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
this.client.on('work_updated', (data) => {
|
|
76
|
-
this.prompt();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
this.client.on('task_assigned', (task) => {
|
|
80
|
-
console.log(chalk.magenta(`\n[New Task] ${task.description}`));
|
|
81
|
-
this.prompt();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
this.client.on('error', (error) => {
|
|
85
|
-
console.error(chalk.red(`\nError: ${error.message}`));
|
|
86
|
-
this.prompt();
|
|
87
|
-
});
|
|
35
|
+
// Event listeners are now handled by AICollaboration class
|
|
88
36
|
}
|
|
89
37
|
|
|
90
38
|
async start() {
|
|
91
39
|
console.clear();
|
|
92
40
|
ui.showHeader();
|
|
93
|
-
console.log(chalk.blue.bold('===
|
|
94
|
-
|
|
95
|
-
// Set agent name
|
|
96
|
-
this.client.name = await this.askQuestion('Enter your name (or press Enter for a random one): ') ||
|
|
97
|
-
`Agent-${Math.random().toString(36).substr(2, 4)}`;
|
|
98
|
-
|
|
99
|
-
// Set agent role
|
|
100
|
-
console.log('\nAvailable roles:');
|
|
101
|
-
Object.entries(MultiplayerClient.AGENT_ROLES).forEach(([key, value]) => {
|
|
102
|
-
console.log(`- ${key}: ${value}`);
|
|
103
|
-
});
|
|
104
|
-
this.client.role = await this.askQuestion('\nChoose your role (or press Enter for Developer): ') ||
|
|
105
|
-
MultiplayerClient.AGENT_ROLES.DEVELOPER;
|
|
106
|
-
|
|
107
|
-
// Get session ID from user
|
|
108
|
-
const sessionId = await this.askQuestion('\nEnter session ID to join or press Enter to create a new session: ');
|
|
41
|
+
console.log(chalk.blue.bold('=== AI Team Collaboration Mode ==='));
|
|
109
42
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
43
|
+
try {
|
|
44
|
+
// Get session ID to join or create a new one
|
|
45
|
+
const sessionId = await this.askQuestion('\nEnter session ID to join (or press Enter to create a new session): ');
|
|
46
|
+
|
|
47
|
+
await this.aiTeam.start(sessionId || undefined);
|
|
48
|
+
|
|
49
|
+
// Keep the process alive
|
|
50
|
+
this.rl.on('close', () => {
|
|
51
|
+
console.log('\nDisconnecting from collaboration session...');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(chalk.red(`\nError: ${error.message}`));
|
|
56
|
+
console.error(error.stack);
|
|
57
|
+
this.rl.close();
|
|
58
|
+
process.exit(1);
|
|
114
59
|
}
|
|
115
|
-
|
|
116
|
-
this.setupCommandHandlers();
|
|
117
|
-
this.prompt();
|
|
118
60
|
}
|
|
119
61
|
|
|
120
62
|
async askQuestion(question) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sam-coder-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
|
|
5
5
|
"main": "bin/agi-cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sam-coder": "bin/agi-cli.js",
|
|
8
|
-
"sam-coder-server": "bin/agi-cli.js --server"
|
|
8
|
+
"sam-coder-server": "bin/agi-cli.js --server",
|
|
9
|
+
"collaborate": "./bin/collaborate.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start": "node ./bin/agi-cli.js"
|
|
@@ -21,8 +22,10 @@
|
|
|
21
22
|
"license": "MIT",
|
|
22
23
|
"dependencies": {
|
|
23
24
|
"chalk": "^4.1.2",
|
|
25
|
+
"commander": "^11.0.0",
|
|
24
26
|
"node-fetch": "^2.6.7",
|
|
25
27
|
"ora": "^5.4.1",
|
|
28
|
+
"readline": "^1.3.0",
|
|
26
29
|
"uuid": "^9.0.0",
|
|
27
30
|
"ws": "^8.18.3"
|
|
28
31
|
},
|