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 +8 -1
- package/cli/install-mcp.ts +125 -0
- package/cli/setup.ts +63 -8
- package/package.json +1 -1
- package/scripts/postinstall.ts +7 -75
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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.
|
|
108
|
-
console.log("\n\x1b[1mConfiguring MCP
|
|
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
package/scripts/postinstall.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
}
|