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 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 v2.0.0
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 (codex uses .mcp.json same as Claude)
112
+ // Configure for Codex CLI (uses .codex/config.toml)
105
113
  function setupCodex(serverPath, cwd) {
106
- const mcpConfigPath = path.join(cwd, '.mcp.json');
107
- let mcpConfig = { mcpServers: {} };
108
- if (fs.existsSync(mcpConfigPath)) {
109
- try {
110
- mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
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
- mcpConfig.mcpServers['agent-bridge'] = {
116
- command: 'node',
117
- args: [serverPath],
118
- env: { AGENT_BRIDGE_DATA_DIR: dataDir(cwd) },
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
- fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
122
- console.log(' [ok] Codex CLI: .mcp.json updated');
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':