let-them-talk 2.0.0 → 3.1.0
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/cli.js +224 -19
- package/dashboard.html +3677 -599
- package/dashboard.js +662 -7
- package/package.json +3 -2
- package/server.js +1379 -130
- package/templates/debate.json +16 -0
- package/templates/pair.json +16 -0
- package/templates/review.json +16 -0
- package/templates/team.json +21 -0
package/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ const command = process.argv[2];
|
|
|
8
8
|
|
|
9
9
|
function printUsage() {
|
|
10
10
|
console.log(`
|
|
11
|
-
Let Them Talk — Agent Bridge
|
|
11
|
+
Let Them Talk — Agent Bridge v3.0.0
|
|
12
12
|
MCP message broker for inter-agent communication
|
|
13
13
|
Supports: Claude Code, Gemini CLI, Codex CLI
|
|
14
14
|
|
|
@@ -18,8 +18,16 @@ function printUsage() {
|
|
|
18
18
|
npx let-them-talk init --gemini Configure for Gemini CLI
|
|
19
19
|
npx let-them-talk init --codex Configure for Codex CLI
|
|
20
20
|
npx let-them-talk init --all Configure for all supported CLIs
|
|
21
|
+
npx let-them-talk init --template T Initialize with a team template (pair, team, review, debate)
|
|
22
|
+
npx let-them-talk templates List available agent templates
|
|
21
23
|
npx let-them-talk dashboard Launch the web dashboard (http://localhost:3000)
|
|
24
|
+
npx let-them-talk dashboard --lan Launch dashboard accessible on LAN (phone/tablet)
|
|
22
25
|
npx let-them-talk reset Clear all conversation data
|
|
26
|
+
npx let-them-talk plugin list List installed plugins
|
|
27
|
+
npx let-them-talk plugin add <file> Install a plugin from a .js file
|
|
28
|
+
npx let-them-talk plugin remove <n> Remove a plugin by name
|
|
29
|
+
npx let-them-talk plugin enable <n> Enable a plugin
|
|
30
|
+
npx let-them-talk plugin disable <n> Disable a plugin
|
|
23
31
|
npx let-them-talk help Show this help message
|
|
24
32
|
`);
|
|
25
33
|
}
|
|
@@ -101,25 +109,36 @@ function setupGemini(serverPath, cwd) {
|
|
|
101
109
|
console.log(' [ok] Gemini CLI: .gemini/settings.json updated');
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
// Configure for Codex CLI (
|
|
112
|
+
// Configure for Codex CLI (uses .codex/config.toml)
|
|
105
113
|
function setupCodex(serverPath, cwd) {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
112
|
-
} catch {}
|
|
114
|
+
const codexDir = path.join(cwd, '.codex');
|
|
115
|
+
const configPath = path.join(codexDir, 'config.toml');
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(codexDir)) {
|
|
118
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
121
|
+
// Read existing config or start fresh
|
|
122
|
+
let config = '';
|
|
123
|
+
if (fs.existsSync(configPath)) {
|
|
124
|
+
config = fs.readFileSync(configPath, 'utf8');
|
|
125
|
+
}
|
|
120
126
|
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
// Only add if not already present
|
|
128
|
+
if (!config.includes('[mcp_servers.agent-bridge]')) {
|
|
129
|
+
const tomlBlock = `
|
|
130
|
+
[mcp_servers.agent-bridge]
|
|
131
|
+
command = "node"
|
|
132
|
+
args = [${JSON.stringify(serverPath)}]
|
|
133
|
+
|
|
134
|
+
[mcp_servers.agent-bridge.env]
|
|
135
|
+
AGENT_BRIDGE_DATA_DIR = ${JSON.stringify(dataDir(cwd))}
|
|
136
|
+
`;
|
|
137
|
+
config += tomlBlock;
|
|
138
|
+
fs.writeFileSync(configPath, config);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(' [ok] Codex CLI: .codex/config.toml updated');
|
|
123
142
|
}
|
|
124
143
|
|
|
125
144
|
function init() {
|
|
@@ -181,10 +200,26 @@ function init() {
|
|
|
181
200
|
|
|
182
201
|
console.log('');
|
|
183
202
|
console.log(' Agent Bridge is ready! Restart your CLI to pick up the MCP tools.');
|
|
184
|
-
console.log(' Open two terminals and start a conversation between agents.');
|
|
185
|
-
console.log('');
|
|
186
|
-
console.log(' Optional: Run "npx let-them-talk dashboard" to monitor conversations.');
|
|
187
203
|
console.log('');
|
|
204
|
+
|
|
205
|
+
// Show template if --template was provided
|
|
206
|
+
var templateFlag = null;
|
|
207
|
+
for (var i = 3; i < process.argv.length; i++) {
|
|
208
|
+
if (process.argv[i] === '--template' && process.argv[i + 1]) {
|
|
209
|
+
templateFlag = process.argv[i + 1];
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (templateFlag) {
|
|
215
|
+
showTemplate(templateFlag);
|
|
216
|
+
} else {
|
|
217
|
+
console.log(' Open two terminals and start a conversation between agents.');
|
|
218
|
+
console.log(' Tip: Use "npx let-them-talk init --template pair" for ready-made prompts.');
|
|
219
|
+
console.log('');
|
|
220
|
+
console.log(' Optional: Run "npx let-them-talk dashboard" to monitor conversations.');
|
|
221
|
+
console.log('');
|
|
222
|
+
}
|
|
188
223
|
}
|
|
189
224
|
|
|
190
225
|
function reset() {
|
|
@@ -206,7 +241,170 @@ function reset() {
|
|
|
206
241
|
console.log(` Cleared ${count} file(s) from ${targetDir}`);
|
|
207
242
|
}
|
|
208
243
|
|
|
244
|
+
function getTemplates() {
|
|
245
|
+
const templatesDir = path.join(__dirname, 'templates');
|
|
246
|
+
if (!fs.existsSync(templatesDir)) return [];
|
|
247
|
+
return fs.readdirSync(templatesDir)
|
|
248
|
+
.filter(f => f.endsWith('.json'))
|
|
249
|
+
.map(f => {
|
|
250
|
+
try { return JSON.parse(fs.readFileSync(path.join(templatesDir, f), 'utf8')); }
|
|
251
|
+
catch { return null; }
|
|
252
|
+
})
|
|
253
|
+
.filter(Boolean);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function listTemplates() {
|
|
257
|
+
const templates = getTemplates();
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(' Available Agent Templates');
|
|
260
|
+
console.log(' ========================');
|
|
261
|
+
console.log('');
|
|
262
|
+
for (const t of templates) {
|
|
263
|
+
const agentNames = t.agents.map(a => a.name).join(', ');
|
|
264
|
+
console.log(' ' + t.name.padEnd(12) + ' ' + t.description);
|
|
265
|
+
console.log(' ' + ''.padEnd(12) + ' Agents: ' + agentNames);
|
|
266
|
+
console.log('');
|
|
267
|
+
}
|
|
268
|
+
console.log(' Usage: npx let-them-talk init --template <name>');
|
|
269
|
+
console.log('');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function showTemplate(templateName) {
|
|
273
|
+
const templates = getTemplates();
|
|
274
|
+
const template = templates.find(t => t.name === templateName);
|
|
275
|
+
if (!template) {
|
|
276
|
+
console.error(' Unknown template: ' + templateName);
|
|
277
|
+
console.error(' Available: ' + templates.map(t => t.name).join(', '));
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log(' Template: ' + template.name);
|
|
283
|
+
console.log(' ' + template.description);
|
|
284
|
+
console.log('');
|
|
285
|
+
console.log(' Copy these prompts into each terminal:');
|
|
286
|
+
console.log(' ======================================');
|
|
287
|
+
|
|
288
|
+
for (var i = 0; i < template.agents.length; i++) {
|
|
289
|
+
var a = template.agents[i];
|
|
290
|
+
console.log('');
|
|
291
|
+
console.log(' --- Terminal ' + (i + 1) + ': ' + a.name + ' (' + a.role + ') ---');
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log(' ' + a.prompt.replace(/\n/g, '\n '));
|
|
294
|
+
console.log('');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function pluginCmd() {
|
|
299
|
+
const subCmd = process.argv[3];
|
|
300
|
+
const dataDir = process.env.AGENT_BRIDGE_DATA_DIR || path.join(process.cwd(), '.agent-bridge');
|
|
301
|
+
const pluginsDir = path.join(dataDir, 'plugins');
|
|
302
|
+
const pluginsFile = path.join(dataDir, 'plugins.json');
|
|
303
|
+
|
|
304
|
+
function getRegistry() {
|
|
305
|
+
if (!fs.existsSync(pluginsFile)) return [];
|
|
306
|
+
try { return JSON.parse(fs.readFileSync(pluginsFile, 'utf8')); } catch { return []; }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function saveRegistry(reg) {
|
|
310
|
+
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
|
311
|
+
fs.writeFileSync(pluginsFile, JSON.stringify(reg, null, 2));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
switch (subCmd) {
|
|
315
|
+
case 'list': {
|
|
316
|
+
const plugins = getRegistry();
|
|
317
|
+
if (!plugins.length) {
|
|
318
|
+
console.log(' No plugins installed.');
|
|
319
|
+
console.log(' Install with: npx let-them-talk plugin add <file.js>');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
console.log('');
|
|
323
|
+
console.log(' Installed Plugins');
|
|
324
|
+
console.log(' =================');
|
|
325
|
+
for (const p of plugins) {
|
|
326
|
+
const status = p.enabled !== false ? 'enabled' : 'disabled';
|
|
327
|
+
console.log(' ' + p.name.padEnd(20) + ' ' + status.padEnd(10) + ' ' + (p.description || ''));
|
|
328
|
+
}
|
|
329
|
+
console.log('');
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case 'add': {
|
|
333
|
+
const filePath = process.argv[4];
|
|
334
|
+
if (!filePath) { console.error(' Usage: npx let-them-talk plugin add <file.js>'); process.exit(1); }
|
|
335
|
+
const absPath = path.resolve(filePath);
|
|
336
|
+
if (!fs.existsSync(absPath)) { console.error(' File not found: ' + absPath); process.exit(1); }
|
|
337
|
+
|
|
338
|
+
// Validate plugin exports
|
|
339
|
+
try {
|
|
340
|
+
const plugin = require(absPath);
|
|
341
|
+
if (!plugin.name || !plugin.handler) { console.error(' Plugin must export name, description, and handler'); process.exit(1); }
|
|
342
|
+
|
|
343
|
+
if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true });
|
|
344
|
+
const destFile = path.join(pluginsDir, path.basename(absPath));
|
|
345
|
+
fs.copyFileSync(absPath, destFile);
|
|
346
|
+
|
|
347
|
+
const reg = getRegistry();
|
|
348
|
+
if (!reg.find(p => p.name === plugin.name)) {
|
|
349
|
+
reg.push({ name: plugin.name, description: plugin.description || '', file: path.basename(absPath), enabled: true, added_at: new Date().toISOString() });
|
|
350
|
+
saveRegistry(reg);
|
|
351
|
+
}
|
|
352
|
+
console.log(' Plugin "' + plugin.name + '" installed successfully.');
|
|
353
|
+
console.log(' Restart CLI to load the new tool.');
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.error(' Failed to load plugin: ' + e.message);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
case 'remove': {
|
|
361
|
+
const name = process.argv[4];
|
|
362
|
+
if (!name) { console.error(' Usage: npx let-them-talk plugin remove <name>'); process.exit(1); }
|
|
363
|
+
const reg = getRegistry();
|
|
364
|
+
const plugin = reg.find(p => p.name === name);
|
|
365
|
+
if (!plugin) { console.error(' Plugin not found: ' + name); process.exit(1); }
|
|
366
|
+
const newReg = reg.filter(p => p.name !== name);
|
|
367
|
+
saveRegistry(newReg);
|
|
368
|
+
if (plugin.file) {
|
|
369
|
+
const pluginFile = path.join(pluginsDir, plugin.file);
|
|
370
|
+
if (fs.existsSync(pluginFile)) fs.unlinkSync(pluginFile);
|
|
371
|
+
}
|
|
372
|
+
console.log(' Plugin "' + name + '" removed.');
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case 'enable': {
|
|
376
|
+
const name = process.argv[4];
|
|
377
|
+
if (!name) { console.error(' Usage: npx let-them-talk plugin enable <name>'); process.exit(1); }
|
|
378
|
+
const reg = getRegistry();
|
|
379
|
+
const plugin = reg.find(p => p.name === name);
|
|
380
|
+
if (!plugin) { console.error(' Plugin not found: ' + name); process.exit(1); }
|
|
381
|
+
plugin.enabled = true;
|
|
382
|
+
saveRegistry(reg);
|
|
383
|
+
console.log(' Plugin "' + name + '" enabled.');
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case 'disable': {
|
|
387
|
+
const name = process.argv[4];
|
|
388
|
+
if (!name) { console.error(' Usage: npx let-them-talk plugin disable <name>'); process.exit(1); }
|
|
389
|
+
const reg = getRegistry();
|
|
390
|
+
const plugin = reg.find(p => p.name === name);
|
|
391
|
+
if (!plugin) { console.error(' Plugin not found: ' + name); process.exit(1); }
|
|
392
|
+
plugin.enabled = false;
|
|
393
|
+
saveRegistry(reg);
|
|
394
|
+
console.log(' Plugin "' + name + '" disabled.');
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
default:
|
|
398
|
+
console.error(' Unknown plugin command: ' + (subCmd || ''));
|
|
399
|
+
console.error(' Available: list, add, remove, enable, disable');
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
209
404
|
function dashboard() {
|
|
405
|
+
if (process.argv.includes('--lan')) {
|
|
406
|
+
process.env.AGENT_BRIDGE_LAN = 'true';
|
|
407
|
+
}
|
|
210
408
|
require('./dashboard.js');
|
|
211
409
|
}
|
|
212
410
|
|
|
@@ -214,12 +412,19 @@ switch (command) {
|
|
|
214
412
|
case 'init':
|
|
215
413
|
init();
|
|
216
414
|
break;
|
|
415
|
+
case 'templates':
|
|
416
|
+
listTemplates();
|
|
417
|
+
break;
|
|
217
418
|
case 'dashboard':
|
|
218
419
|
dashboard();
|
|
219
420
|
break;
|
|
220
421
|
case 'reset':
|
|
221
422
|
reset();
|
|
222
423
|
break;
|
|
424
|
+
case 'plugin':
|
|
425
|
+
case 'plugins':
|
|
426
|
+
pluginCmd();
|
|
427
|
+
break;
|
|
223
428
|
case 'help':
|
|
224
429
|
case '--help':
|
|
225
430
|
case '-h':
|