let-them-talk 2.5.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.
Files changed (5) hide show
  1. package/cli.js +146 -16
  2. package/dashboard.html +1776 -62
  3. package/dashboard.js +402 -24
  4. package/package.json +1 -1
  5. package/server.js +808 -43
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.5.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
 
@@ -21,7 +21,13 @@ function printUsage() {
21
21
  npx let-them-talk init --template T Initialize with a team template (pair, team, review, debate)
22
22
  npx let-them-talk templates List available agent templates
23
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)
24
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
25
31
  npx let-them-talk help Show this help message
26
32
  `);
27
33
  }
@@ -103,25 +109,36 @@ function setupGemini(serverPath, cwd) {
103
109
  console.log(' [ok] Gemini CLI: .gemini/settings.json updated');
104
110
  }
105
111
 
106
- // Configure for Codex CLI (codex uses .mcp.json same as Claude)
112
+ // Configure for Codex CLI (uses .codex/config.toml)
107
113
  function setupCodex(serverPath, cwd) {
108
- const mcpConfigPath = path.join(cwd, '.mcp.json');
109
- let mcpConfig = { mcpServers: {} };
110
- if (fs.existsSync(mcpConfigPath)) {
111
- try {
112
- mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
113
- if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
114
- } 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 });
115
119
  }
116
120
 
117
- mcpConfig.mcpServers['agent-bridge'] = {
118
- command: 'node',
119
- args: [serverPath],
120
- env: { AGENT_BRIDGE_DATA_DIR: dataDir(cwd) },
121
- };
121
+ // Read existing config or start fresh
122
+ let config = '';
123
+ if (fs.existsSync(configPath)) {
124
+ config = fs.readFileSync(configPath, 'utf8');
125
+ }
122
126
 
123
- fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
124
- 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');
125
142
  }
126
143
 
127
144
  function init() {
@@ -278,7 +295,116 @@ function showTemplate(templateName) {
278
295
  }
279
296
  }
280
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
+
281
404
  function dashboard() {
405
+ if (process.argv.includes('--lan')) {
406
+ process.env.AGENT_BRIDGE_LAN = 'true';
407
+ }
282
408
  require('./dashboard.js');
283
409
  }
284
410
 
@@ -295,6 +421,10 @@ switch (command) {
295
421
  case 'reset':
296
422
  reset();
297
423
  break;
424
+ case 'plugin':
425
+ case 'plugins':
426
+ pluginCmd();
427
+ break;
298
428
  case 'help':
299
429
  case '--help':
300
430
  case '-h':