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 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
+ }