multiagents 0.1.0 → 0.1.2

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/commands.ts CHANGED
@@ -13,7 +13,8 @@ function printUsage(): void {
13
13
  console.log(`multiagents CLI
14
14
 
15
15
  Usage:
16
- multiagents setup Interactive setup wizard
16
+ multiagents install-mcp Configure MCP servers for Claude Code
17
+ multiagents setup Interactive setup wizard
17
18
  multiagents dashboard [session-id] TUI dashboard
18
19
  multiagents session create <name> Create a new session
19
20
  multiagents session list List all sessions
@@ -35,6 +36,12 @@ export async function runCli(args: string[]): Promise<void> {
35
36
  const command = args[0];
36
37
 
37
38
  switch (command) {
39
+ case "install-mcp": {
40
+ const { installMcp } = await import("./install-mcp.ts");
41
+ await installMcp();
42
+ break;
43
+ }
44
+
38
45
  case "setup": {
39
46
  const { setup } = await import("./setup.ts");
40
47
  await setup();
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * multiagents install-mcp — Configure MCP servers for Claude Code.
4
+ *
5
+ * 1. Writes multiagents + multiagents-orch to ~/.claude/.mcp.json
6
+ * 2. Adds them to enabledMcpjsonServers in ~/.claude/settings.json
7
+ * 3. Prints instructions to restart Claude Code
8
+ *
9
+ * Uses the installed binary names (multiagents-server, multiagents-orch)
10
+ * so it works regardless of where the package is installed.
11
+ */
12
+
13
+ import * as fs from "node:fs";
14
+ import * as path from "node:path";
15
+ import * as os from "node:os";
16
+
17
+ const HOME = os.homedir();
18
+ const CLAUDE_DIR = path.join(HOME, ".claude");
19
+ const MCP_JSON = path.join(CLAUDE_DIR, ".mcp.json");
20
+ const SETTINGS_JSON = path.join(CLAUDE_DIR, "settings.json");
21
+
22
+ function findBinary(name: string): string {
23
+ try {
24
+ const which = Bun.spawnSync(["which", name]);
25
+ const found = new TextDecoder().decode(which.stdout).trim();
26
+ if (found) return found;
27
+ } catch { /* ok */ }
28
+
29
+ const candidates = [
30
+ path.join(HOME, ".bun", "bin", name),
31
+ `/usr/local/bin/${name}`,
32
+ `/opt/homebrew/bin/${name}`,
33
+ ];
34
+ for (const c of candidates) {
35
+ if (fs.existsSync(c)) return c;
36
+ }
37
+ return name;
38
+ }
39
+
40
+ /** Core logic — configures .mcp.json + settings.json. Returns log lines. */
41
+ function configureMcp(): string[] {
42
+ const logs: string[] = [];
43
+ const serverBin = findBinary("multiagents-server");
44
+ const orchBin = findBinary("multiagents-orch");
45
+
46
+ // Step 1: Write ~/.claude/.mcp.json
47
+ if (!fs.existsSync(CLAUDE_DIR)) {
48
+ fs.mkdirSync(CLAUDE_DIR, { recursive: true });
49
+ }
50
+
51
+ let mcpConfig: { mcpServers: Record<string, unknown> } = { mcpServers: {} };
52
+ if (fs.existsSync(MCP_JSON)) {
53
+ try {
54
+ mcpConfig = JSON.parse(fs.readFileSync(MCP_JSON, "utf-8"));
55
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
56
+ } catch {
57
+ fs.copyFileSync(MCP_JSON, MCP_JSON + ".bak");
58
+ mcpConfig = { mcpServers: {} };
59
+ logs.push(" \x1b[33m!\x1b[0m Existing .mcp.json was corrupted — backed up and recreated");
60
+ }
61
+ }
62
+
63
+ mcpConfig.mcpServers["multiagents"] = { command: serverBin, args: [] };
64
+ mcpConfig.mcpServers["multiagents-orch"] = { command: orchBin, args: [] };
65
+ fs.writeFileSync(MCP_JSON, JSON.stringify(mcpConfig, null, 2) + "\n");
66
+ logs.push(" \x1b[32m✔\x1b[0m Global MCP configured (~/.claude/.mcp.json)");
67
+
68
+ // Step 2: Enable in ~/.claude/settings.json
69
+ let settings: Record<string, unknown> = {};
70
+ if (fs.existsSync(SETTINGS_JSON)) {
71
+ try {
72
+ settings = JSON.parse(fs.readFileSync(SETTINGS_JSON, "utf-8"));
73
+ } catch {
74
+ logs.push(" \x1b[33m!\x1b[0m Could not parse settings.json — skipping auto-enable");
75
+ return logs;
76
+ }
77
+ }
78
+
79
+ const enabled = (settings.enabledMcpjsonServers as string[]) ?? [];
80
+ let changed = false;
81
+ if (!enabled.includes("multiagents")) { enabled.push("multiagents"); changed = true; }
82
+ if (!enabled.includes("multiagents-orch")) { enabled.push("multiagents-orch"); changed = true; }
83
+ if (changed) {
84
+ settings.enabledMcpjsonServers = enabled;
85
+ fs.writeFileSync(SETTINGS_JSON, JSON.stringify(settings, null, 2) + "\n");
86
+ logs.push(" \x1b[32m✔\x1b[0m Enabled in ~/.claude/settings.json");
87
+ } else {
88
+ logs.push(" \x1b[32m✔\x1b[0m Already enabled in settings.json");
89
+ }
90
+
91
+ return logs;
92
+ }
93
+
94
+ /** Verbose version — standalone `multiagents install-mcp` command. */
95
+ export async function installMcp(): Promise<void> {
96
+ console.log("\n\x1b[1m\x1b[36m multiagents install-mcp\x1b[0m");
97
+ console.log("\x1b[90m Configure MCP servers for Claude Code\x1b[0m\n");
98
+
99
+ const logs = configureMcp();
100
+ for (const line of logs) console.log(line);
101
+
102
+ console.log(`
103
+ \x1b[1m\x1b[32mDone!\x1b[0m MCP servers configured.
104
+
105
+ \x1b[1mNext step:\x1b[0m Restart Claude Code to load the new tools.
106
+ Exit Claude Code and run: \x1b[90mclaude\x1b[0m
107
+
108
+ \x1b[1mManual setup:\x1b[0m If auto-config doesn't work, add to ~/.claude/.mcp.json:
109
+ \x1b[90m {
110
+ "mcpServers": {
111
+ "multiagents": { "command": "multiagents-server", "args": [] },
112
+ "multiagents-orch": { "command": "multiagents-orch", "args": [] }
113
+ }
114
+ }\x1b[0m
115
+
116
+ And add to ~/.claude/settings.json:
117
+ \x1b[90m "enabledMcpjsonServers": ["multiagents", "multiagents-orch"]\x1b[0m
118
+ `);
119
+ }
120
+
121
+ /** Quiet version — called from `setup` flow, prints compact output. */
122
+ export async function installMcpSilent(): Promise<void> {
123
+ const logs = configureMcp();
124
+ for (const line of logs) console.log(line);
125
+ }
package/cli/setup.ts CHANGED
@@ -9,6 +9,7 @@ import type { AgentType, SessionFile } from "../shared/types.ts";
9
9
  import * as readline from "node:readline";
10
10
  import * as path from "node:path";
11
11
  import * as fs from "node:fs";
12
+ import * as os from "node:os";
12
13
 
13
14
  const BROKER_PORT = parseInt(process.env.MULTIAGENTS_PORT ?? String(DEFAULT_BROKER_PORT), 10);
14
15
  const BROKER_URL = `http://${BROKER_HOSTNAME}:${BROKER_PORT}`;
@@ -26,17 +27,66 @@ function prompt(question: string, defaultValue?: string): Promise<string> {
26
27
  });
27
28
  }
28
29
 
29
- function detectAgent(name: string): { available: boolean; version?: string } {
30
+ /** Known install locations per agent type, checked as fallbacks when `which` fails. */
31
+ const KNOWN_PATHS: Record<string, string[]> = {
32
+ claude: [
33
+ path.join(os.homedir(), ".local", "bin", "claude"),
34
+ path.join(os.homedir(), ".claude", "bin", "claude"),
35
+ "/usr/local/bin/claude",
36
+ "/opt/homebrew/bin/claude",
37
+ ],
38
+ codex: [
39
+ "/usr/local/bin/codex",
40
+ path.join(os.homedir(), ".local", "bin", "codex"),
41
+ path.join(os.homedir(), ".npm-global", "bin", "codex"),
42
+ "/opt/homebrew/bin/codex",
43
+ ],
44
+ gemini: [
45
+ path.join(os.homedir(), ".local", "bin", "gemini"),
46
+ "/usr/local/bin/gemini",
47
+ "/opt/homebrew/bin/gemini",
48
+ path.join(os.homedir(), ".npm-global", "bin", "gemini"),
49
+ ],
50
+ };
51
+
52
+ function detectAgent(name: string): { available: boolean; version?: string; resolvedPath?: string } {
53
+ // 1. Try `which`
54
+ let agentPath: string | null = null;
30
55
  try {
31
56
  const which = Bun.spawnSync(["which", name]);
32
- if (which.exitCode !== 0) return { available: false };
57
+ if (which.exitCode === 0) {
58
+ agentPath = new TextDecoder().decode(which.stdout).trim();
59
+ }
60
+ } catch { /* which failed */ }
61
+
62
+ // 2. Fallback: check known paths
63
+ if (!agentPath) {
64
+ const candidates = KNOWN_PATHS[name] ?? [];
65
+ for (const p of candidates) {
66
+ if (fs.existsSync(p)) {
67
+ agentPath = p;
68
+ break;
69
+ }
70
+ }
71
+ }
72
+
73
+ if (!agentPath) return { available: false };
33
74
 
34
- const ver = Bun.spawnSync([name, "--version"]);
35
- const version = new TextDecoder().decode(ver.stdout).trim().split("\n")[0];
36
- return { available: true, version: version || undefined };
75
+ // 3. Get version (with timeout protection — some CLIs hang)
76
+ let version: string | undefined;
77
+ try {
78
+ const ver = Bun.spawnSync([agentPath, "--version"], {
79
+ timeout: 5000, // 5s timeout
80
+ });
81
+ if (ver.exitCode === 0) {
82
+ version = new TextDecoder().decode(ver.stdout).trim().split("\n")[0] || undefined;
83
+ }
37
84
  } catch {
38
- return { available: false };
85
+ // Version check failed — agent exists but version unknown
86
+ version = undefined;
39
87
  }
88
+
89
+ return { available: true, version, resolvedPath: agentPath };
40
90
  }
41
91
 
42
92
  export async function setup(): Promise<void> {
@@ -104,8 +154,13 @@ export async function setup(): Promise<void> {
104
154
  const sessionName = await prompt("Session name", dirName);
105
155
  const sessionId = slugify(sessionName);
106
156
 
107
- // 6. Configure each selected agent's MCP server
108
- console.log("\n\x1b[1mConfiguring MCP servers...\x1b[0m\n");
157
+ // 6. Install global MCP config (Claude Code + settings.json)
158
+ console.log("\n\x1b[1mConfiguring global MCP...\x1b[0m\n");
159
+ const { installMcpSilent } = await import("./install-mcp.ts");
160
+ await installMcpSilent();
161
+
162
+ // 7. Configure per-agent MCP
163
+ console.log("\x1b[1mConfiguring per-agent MCP...\x1b[0m\n");
109
164
 
110
165
  const cliPath = path.resolve(import.meta.dir, "..");
111
166
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multiagents",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Multi-agent orchestration platform for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -1,84 +1,16 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
3
  * Post-install script: auto-configures MCP servers for Claude Code.
4
- *
5
- * Adds multiagents and multiagents-orch to ~/.claude/.mcp.json
6
- * so Claude Code can discover the tools without manual configuration.
7
- *
8
- * Safe: only adds entries if they don't already exist. Never overwrites.
4
+ * Delegates to the same logic as `multiagents install-mcp`.
5
+ * If it fails, prints fallback instructions — never breaks the install.
9
6
  */
10
7
 
11
- import * as fs from "node:fs";
12
- import * as path from "node:path";
13
- import * as os from "node:os";
14
-
15
- const HOME = os.homedir();
16
- const CLAUDE_DIR = path.join(HOME, ".claude");
17
- const MCP_JSON = path.join(CLAUDE_DIR, ".mcp.json");
18
-
19
- // Resolve the installed package's bin paths
20
- const PKG_ROOT = path.resolve(import.meta.dir, "..");
21
- const SERVER_PATH = path.join(PKG_ROOT, "server.ts");
22
- const ORCH_PATH = path.join(PKG_ROOT, "orchestrator", "orchestrator-server.ts");
23
-
24
- // Find bun binary
25
- function findBun(): string {
26
- const bunInPath = Bun.spawnSync(["which", "bun"]).stdout.toString().trim();
27
- if (bunInPath) return bunInPath;
28
- const defaultBun = path.join(HOME, ".bun", "bin", "bun");
29
- if (fs.existsSync(defaultBun)) return defaultBun;
30
- return "bun"; // fallback — hope it's in PATH at runtime
31
- }
32
-
33
- const BUN = findBun();
34
-
35
8
  try {
36
- // Ensure ~/.claude/ exists
37
- if (!fs.existsSync(CLAUDE_DIR)) {
38
- fs.mkdirSync(CLAUDE_DIR, { recursive: true });
39
- }
40
-
41
- // Read or create .mcp.json
42
- let config: { mcpServers: Record<string, unknown> } = { mcpServers: {} };
43
- if (fs.existsSync(MCP_JSON)) {
44
- try {
45
- config = JSON.parse(fs.readFileSync(MCP_JSON, "utf-8"));
46
- if (!config.mcpServers) config.mcpServers = {};
47
- } catch {
48
- // Corrupted file — back up and recreate
49
- fs.copyFileSync(MCP_JSON, MCP_JSON + ".bak");
50
- config = { mcpServers: {} };
51
- }
52
- }
53
-
54
- let changed = false;
55
-
56
- // Add multiagents MCP server
57
- if (!config.mcpServers["multiagents"]) {
58
- config.mcpServers["multiagents"] = {
59
- command: BUN,
60
- args: [SERVER_PATH],
61
- };
62
- changed = true;
63
- }
64
-
65
- // Add orchestrator MCP server
66
- if (!config.mcpServers["multiagents-orch"]) {
67
- config.mcpServers["multiagents-orch"] = {
68
- command: BUN,
69
- args: [ORCH_PATH],
70
- };
71
- changed = true;
72
- }
73
-
74
- if (changed) {
75
- fs.writeFileSync(MCP_JSON, JSON.stringify(config, null, 2) + "\n");
76
- console.log("[multiagents] MCP servers configured in ~/.claude/.mcp.json");
77
- console.log(" Restart Claude Code to pick up the new tools.");
78
- } else {
79
- console.log("[multiagents] MCP servers already configured.");
80
- }
9
+ const { installMcpSilent } = await import("../cli/install-mcp.ts");
10
+ await installMcpSilent();
11
+ console.log("[multiagents] Restart Claude Code to pick up the new tools.");
12
+ console.log("[multiagents] If tools don't appear, run: multiagents install-mcp");
81
13
  } catch (e) {
82
- // Postinstall should never fail the install
83
14
  console.error(`[multiagents] postinstall warning: ${e instanceof Error ? e.message : String(e)}`);
15
+ console.error("[multiagents] Run 'multiagents install-mcp' to configure manually.");
84
16
  }