let-them-talk 2.0.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 +233 -0
- package/dashboard.html +1719 -0
- package/dashboard.js +359 -0
- package/package.json +46 -0
- package/server.js +727 -0
package/cli.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const command = process.argv[2];
|
|
8
|
+
|
|
9
|
+
function printUsage() {
|
|
10
|
+
console.log(`
|
|
11
|
+
Let Them Talk — Agent Bridge v2.0.0
|
|
12
|
+
MCP message broker for inter-agent communication
|
|
13
|
+
Supports: Claude Code, Gemini CLI, Codex CLI
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
npx let-them-talk init Auto-detect CLI and configure MCP
|
|
17
|
+
npx let-them-talk init --claude Configure for Claude Code
|
|
18
|
+
npx let-them-talk init --gemini Configure for Gemini CLI
|
|
19
|
+
npx let-them-talk init --codex Configure for Codex CLI
|
|
20
|
+
npx let-them-talk init --all Configure for all supported CLIs
|
|
21
|
+
npx let-them-talk dashboard Launch the web dashboard (http://localhost:3000)
|
|
22
|
+
npx let-them-talk reset Clear all conversation data
|
|
23
|
+
npx let-them-talk help Show this help message
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Detect which CLIs are installed
|
|
28
|
+
function detectCLIs() {
|
|
29
|
+
const detected = [];
|
|
30
|
+
const home = os.homedir();
|
|
31
|
+
|
|
32
|
+
// Claude Code: ~/.claude/ directory exists
|
|
33
|
+
if (fs.existsSync(path.join(home, '.claude'))) {
|
|
34
|
+
detected.push('claude');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Gemini CLI: ~/.gemini/ or GEMINI_API_KEY set
|
|
38
|
+
if (fs.existsSync(path.join(home, '.gemini')) || process.env.GEMINI_API_KEY) {
|
|
39
|
+
detected.push('gemini');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Codex CLI: ~/.codex/ directory exists
|
|
43
|
+
if (fs.existsSync(path.join(home, '.codex'))) {
|
|
44
|
+
detected.push('codex');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return detected;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The data directory where all agents read/write — must be the same for server + dashboard
|
|
51
|
+
function dataDir(cwd) {
|
|
52
|
+
return path.join(cwd, '.agent-bridge').replace(/\\/g, '/');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Configure for Claude Code (.mcp.json in project root)
|
|
56
|
+
function setupClaude(serverPath, cwd) {
|
|
57
|
+
const mcpConfigPath = path.join(cwd, '.mcp.json');
|
|
58
|
+
let mcpConfig = { mcpServers: {} };
|
|
59
|
+
if (fs.existsSync(mcpConfigPath)) {
|
|
60
|
+
try {
|
|
61
|
+
mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
|
|
62
|
+
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
mcpConfig.mcpServers['agent-bridge'] = {
|
|
67
|
+
command: 'node',
|
|
68
|
+
args: [serverPath],
|
|
69
|
+
env: { AGENT_BRIDGE_DATA_DIR: dataDir(cwd) },
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
73
|
+
console.log(' [ok] Claude Code: .mcp.json updated');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Configure for Gemini CLI (.gemini/settings.json or GEMINI.md with MCP config)
|
|
77
|
+
function setupGemini(serverPath, cwd) {
|
|
78
|
+
// Gemini CLI uses .gemini/settings.json for MCP configuration
|
|
79
|
+
const geminiDir = path.join(cwd, '.gemini');
|
|
80
|
+
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
81
|
+
|
|
82
|
+
if (!fs.existsSync(geminiDir)) {
|
|
83
|
+
fs.mkdirSync(geminiDir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let settings = { mcpServers: {} };
|
|
87
|
+
if (fs.existsSync(settingsPath)) {
|
|
88
|
+
try {
|
|
89
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
90
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
settings.mcpServers['agent-bridge'] = {
|
|
95
|
+
command: 'node',
|
|
96
|
+
args: [serverPath],
|
|
97
|
+
env: { AGENT_BRIDGE_DATA_DIR: dataDir(cwd) },
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
101
|
+
console.log(' [ok] Gemini CLI: .gemini/settings.json updated');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Configure for Codex CLI (codex uses .mcp.json same as Claude)
|
|
105
|
+
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 {}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
mcpConfig.mcpServers['agent-bridge'] = {
|
|
116
|
+
command: 'node',
|
|
117
|
+
args: [serverPath],
|
|
118
|
+
env: { AGENT_BRIDGE_DATA_DIR: dataDir(cwd) },
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
122
|
+
console.log(' [ok] Codex CLI: .mcp.json updated');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function init() {
|
|
126
|
+
const cwd = process.cwd();
|
|
127
|
+
const serverPath = path.join(__dirname, 'server.js').replace(/\\/g, '/');
|
|
128
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
129
|
+
const flag = process.argv[3];
|
|
130
|
+
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(' Let Them Talk — Initializing Agent Bridge');
|
|
133
|
+
console.log(' ==========================================');
|
|
134
|
+
console.log('');
|
|
135
|
+
|
|
136
|
+
let targets = [];
|
|
137
|
+
|
|
138
|
+
if (flag === '--claude') {
|
|
139
|
+
targets = ['claude'];
|
|
140
|
+
} else if (flag === '--gemini') {
|
|
141
|
+
targets = ['gemini'];
|
|
142
|
+
} else if (flag === '--codex') {
|
|
143
|
+
targets = ['codex'];
|
|
144
|
+
} else if (flag === '--all') {
|
|
145
|
+
targets = ['claude', 'gemini', 'codex'];
|
|
146
|
+
} else {
|
|
147
|
+
// Auto-detect
|
|
148
|
+
targets = detectCLIs();
|
|
149
|
+
if (targets.length === 0) {
|
|
150
|
+
// Default to claude if nothing detected
|
|
151
|
+
targets = ['claude'];
|
|
152
|
+
console.log(' No CLI detected, defaulting to Claude Code config.');
|
|
153
|
+
} else {
|
|
154
|
+
console.log(` Detected CLI(s): ${targets.join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log('');
|
|
159
|
+
|
|
160
|
+
for (const target of targets) {
|
|
161
|
+
switch (target) {
|
|
162
|
+
case 'claude': setupClaude(serverPath, cwd); break;
|
|
163
|
+
case 'gemini': setupGemini(serverPath, cwd); break;
|
|
164
|
+
case 'codex': setupCodex(serverPath, cwd); break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Add .agent-bridge/ to .gitignore
|
|
169
|
+
if (fs.existsSync(gitignorePath)) {
|
|
170
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
171
|
+
if (!content.includes('.agent-bridge')) {
|
|
172
|
+
fs.appendFileSync(gitignorePath, '\n# Agent Bridge conversation data\n.agent-bridge/\n');
|
|
173
|
+
console.log(' [ok] .agent-bridge/ added to .gitignore');
|
|
174
|
+
} else {
|
|
175
|
+
console.log(' [ok] .agent-bridge/ already in .gitignore');
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
fs.writeFileSync(gitignorePath, '# Agent Bridge conversation data\n.agent-bridge/\n');
|
|
179
|
+
console.log(' [ok] .gitignore created with .agent-bridge/');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log('');
|
|
183
|
+
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
|
+
console.log('');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function reset() {
|
|
191
|
+
const dataDir = process.env.AGENT_BRIDGE_DATA_DIR || path.join(process.cwd(), '.agent-bridge');
|
|
192
|
+
// Also check legacy data/ dir
|
|
193
|
+
const legacyDir = path.join(process.cwd(), 'data');
|
|
194
|
+
const targetDir = fs.existsSync(dataDir) ? dataDir : fs.existsSync(legacyDir) ? legacyDir : dataDir;
|
|
195
|
+
|
|
196
|
+
if (!fs.existsSync(targetDir)) {
|
|
197
|
+
console.log(' No data directory found. Nothing to reset.');
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const files = fs.readdirSync(targetDir);
|
|
201
|
+
let count = 0;
|
|
202
|
+
for (const f of files) {
|
|
203
|
+
fs.unlinkSync(path.join(targetDir, f));
|
|
204
|
+
count++;
|
|
205
|
+
}
|
|
206
|
+
console.log(` Cleared ${count} file(s) from ${targetDir}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function dashboard() {
|
|
210
|
+
require('./dashboard.js');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
switch (command) {
|
|
214
|
+
case 'init':
|
|
215
|
+
init();
|
|
216
|
+
break;
|
|
217
|
+
case 'dashboard':
|
|
218
|
+
dashboard();
|
|
219
|
+
break;
|
|
220
|
+
case 'reset':
|
|
221
|
+
reset();
|
|
222
|
+
break;
|
|
223
|
+
case 'help':
|
|
224
|
+
case '--help':
|
|
225
|
+
case '-h':
|
|
226
|
+
case undefined:
|
|
227
|
+
printUsage();
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
console.error(` Unknown command: ${command}`);
|
|
231
|
+
printUsage();
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|