dispatch-agents 0.4.1 → 0.5.1
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/README.md +35 -0
- package/dist/cli.js +28 -32
- package/dist/mcp.js +263 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -49,6 +49,41 @@ npm link
|
|
|
49
49
|
- `git` — for worktree management
|
|
50
50
|
- iTerm2 (recommended) — for native tab integration via `tmux -CC`
|
|
51
51
|
|
|
52
|
+
## MCP Server
|
|
53
|
+
|
|
54
|
+
Dispatch includes an MCP server so Claude Code can orchestrate agents directly — no shell commands needed.
|
|
55
|
+
|
|
56
|
+
### Setup
|
|
57
|
+
|
|
58
|
+
After installing dispatch, register the MCP server with Claude Code:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
claude mcp add --scope user dispatch node $(which dispatch-mcp)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This exposes 6 tools to Claude Code:
|
|
65
|
+
|
|
66
|
+
| Tool | Description |
|
|
67
|
+
|------|-------------|
|
|
68
|
+
| `dispatch_run` | Launch an agent with a prompt (inline text, not a file) |
|
|
69
|
+
| `dispatch_list` | List all running agents with status |
|
|
70
|
+
| `dispatch_stop` | Stop a running agent |
|
|
71
|
+
| `dispatch_resume` | Resume a stopped agent |
|
|
72
|
+
| `dispatch_cleanup` | Remove worktrees and optionally branches |
|
|
73
|
+
| `dispatch_logs` | Get recent output from an agent |
|
|
74
|
+
|
|
75
|
+
### How it works
|
|
76
|
+
|
|
77
|
+
The MCP server wraps the dispatch CLI over stdio using the [Model Context Protocol](https://modelcontextprotocol.io). When Claude Code calls `dispatch_run`, the server writes the prompt to a temp file, runs `dispatch run --prompt-file <file>`, and cleans up. Interactive agents open iTerm tabs; headless agents run in the background.
|
|
78
|
+
|
|
79
|
+
### Working directory
|
|
80
|
+
|
|
81
|
+
By default the MCP server uses the directory Claude Code is running in. To override, set `DISPATCH_CWD`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
claude mcp add --scope user dispatch -e DISPATCH_CWD=/path/to/repo node $(which dispatch-mcp)
|
|
85
|
+
```
|
|
86
|
+
|
|
52
87
|
## Usage
|
|
53
88
|
|
|
54
89
|
### Launch an agent
|
package/dist/cli.js
CHANGED
|
@@ -211,8 +211,10 @@ function ensureSession() {
|
|
|
211
211
|
execSync(
|
|
212
212
|
`tmux new-session -d -s "${DISPATCH_SESSION}" -n "dispatch"`
|
|
213
213
|
);
|
|
214
|
-
execSync(`tmux set -t "${DISPATCH_SESSION}"
|
|
215
|
-
execSync(`tmux set -t "${DISPATCH_SESSION}"
|
|
214
|
+
execSync(`tmux set -t "${DISPATCH_SESSION}" mouse on`);
|
|
215
|
+
execSync(`tmux set -t "${DISPATCH_SESSION}" history-limit 50000`);
|
|
216
|
+
execQuiet(`tmux set -t "${DISPATCH_SESSION}" set-titles on`);
|
|
217
|
+
execQuiet(`tmux set -t "${DISPATCH_SESSION}" set-titles-string "#W"`);
|
|
216
218
|
execSync(
|
|
217
219
|
`tmux send-keys -t "${DISPATCH_SESSION}:dispatch" "# Dispatch control window" Enter`
|
|
218
220
|
);
|
|
@@ -241,6 +243,9 @@ function createWindow(id, cwd) {
|
|
|
241
243
|
log.error(`Failed to create tmux window: ${err}`);
|
|
242
244
|
process.exit(1);
|
|
243
245
|
}
|
|
246
|
+
const target = `${DISPATCH_SESSION}:${id}`;
|
|
247
|
+
execQuiet(`tmux setw -t "${target}" allow-passthrough on`);
|
|
248
|
+
execQuiet(`tmux setw -t "${target}" automatic-rename off`);
|
|
244
249
|
const countStr = execQuiet(
|
|
245
250
|
`tmux list-windows -t "${DISPATCH_SESSION}" | wc -l`
|
|
246
251
|
);
|
|
@@ -249,19 +254,9 @@ function createWindow(id, cwd) {
|
|
|
249
254
|
const red = parseInt(hex.slice(0, 2), 16);
|
|
250
255
|
const green = parseInt(hex.slice(2, 4), 16);
|
|
251
256
|
const blue = parseInt(hex.slice(4, 6), 16);
|
|
252
|
-
const target = `${DISPATCH_SESSION}:${id}`;
|
|
253
|
-
execSync(
|
|
254
|
-
`tmux send-keys -t "${target}" "printf '\\\\033]6;1;bg;red;brightness;${red}\\\\007'" Enter`
|
|
255
|
-
);
|
|
256
|
-
execSync(
|
|
257
|
-
`tmux send-keys -t "${target}" "printf '\\\\033]6;1;bg;green;brightness;${green}\\\\007'" Enter`
|
|
258
|
-
);
|
|
259
|
-
execSync(
|
|
260
|
-
`tmux send-keys -t "${target}" "printf '\\\\033]6;1;bg;blue;brightness;${blue}\\\\007'" Enter`
|
|
261
|
-
);
|
|
262
257
|
const badge = Buffer.from(id).toString("base64");
|
|
263
258
|
execSync(
|
|
264
|
-
`tmux send-keys -t "${target}" "printf '\\\\033]1337;SetBadgeFormat=${badge}\\\\007'" Enter`
|
|
259
|
+
`tmux send-keys -t "${target}" "printf '\\\\033]0;${id}\\\\007\\\\033]6;1;bg;red;brightness;${red}\\\\007\\\\033]6;1;bg;green;brightness;${green}\\\\007\\\\033]6;1;bg;blue;brightness;${blue}\\\\007\\\\033]1337;SetBadgeFormat=${badge}\\\\007' && clear" Enter`
|
|
265
260
|
);
|
|
266
261
|
return true;
|
|
267
262
|
}
|
|
@@ -294,19 +289,12 @@ function tmuxAttach(window) {
|
|
|
294
289
|
const target = window ? `${DISPATCH_SESSION}:${window}` : DISPATCH_SESSION;
|
|
295
290
|
const hasTTY = process.stdin.isTTY;
|
|
296
291
|
if (hasTTY) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
});
|
|
302
|
-
} else {
|
|
303
|
-
spawnSync("tmux", ["attach", "-t", target], {
|
|
304
|
-
stdio: "inherit"
|
|
305
|
-
});
|
|
306
|
-
}
|
|
292
|
+
spawnSync("tmux", ["attach", "-t", target], {
|
|
293
|
+
stdio: "inherit",
|
|
294
|
+
env: { ...process.env, TERM_PROGRAM: "dumb" }
|
|
295
|
+
});
|
|
307
296
|
} else if (process.platform === "darwin") {
|
|
308
|
-
const
|
|
309
|
-
const script = openTerminalTabAppleScript(cmd);
|
|
297
|
+
const script = openTerminalTabAppleScript(target);
|
|
310
298
|
if (script) {
|
|
311
299
|
spawnSync("osascript", ["-e", script], { stdio: "pipe" });
|
|
312
300
|
} else {
|
|
@@ -316,7 +304,8 @@ function tmuxAttach(window) {
|
|
|
316
304
|
log.warn(`No TTY available. Run manually: tmux attach -t ${target}`);
|
|
317
305
|
}
|
|
318
306
|
}
|
|
319
|
-
function openTerminalTabAppleScript(
|
|
307
|
+
function openTerminalTabAppleScript(target) {
|
|
308
|
+
const agentName = target.includes(":") ? target.split(":").pop() : target;
|
|
320
309
|
const terminals = [
|
|
321
310
|
{
|
|
322
311
|
name: "iTerm2",
|
|
@@ -326,7 +315,8 @@ function openTerminalTabAppleScript(command) {
|
|
|
326
315
|
tell current window
|
|
327
316
|
create tab with default profile
|
|
328
317
|
tell current session
|
|
329
|
-
|
|
318
|
+
set name to "${agentName}"
|
|
319
|
+
write text "TERM_PROGRAM=dumb tmux attach -t ${target}"
|
|
330
320
|
end tell
|
|
331
321
|
end tell
|
|
332
322
|
end tell`
|
|
@@ -339,7 +329,7 @@ function openTerminalTabAppleScript(command) {
|
|
|
339
329
|
tell application "System Events" to tell process "Warp"
|
|
340
330
|
keystroke "t" using command down
|
|
341
331
|
delay 0.3
|
|
342
|
-
keystroke "${
|
|
332
|
+
keystroke "tmux attach -t ${target}"
|
|
343
333
|
key code 36
|
|
344
334
|
end tell
|
|
345
335
|
end tell`
|
|
@@ -349,7 +339,7 @@ function openTerminalTabAppleScript(command) {
|
|
|
349
339
|
bundleId: "com.apple.Terminal",
|
|
350
340
|
script: `tell application "Terminal"
|
|
351
341
|
activate
|
|
352
|
-
do script "${
|
|
342
|
+
do script "tmux attach -t ${target}"
|
|
353
343
|
end tell`
|
|
354
344
|
}
|
|
355
345
|
];
|
|
@@ -539,6 +529,7 @@ async function cmdRun(args, config) {
|
|
|
539
529
|
let extraArgs = "";
|
|
540
530
|
let skipWorktree = false;
|
|
541
531
|
let nameOverride = "";
|
|
532
|
+
let noAttach = false;
|
|
542
533
|
let i = 0;
|
|
543
534
|
while (i < args.length) {
|
|
544
535
|
const arg = args[i];
|
|
@@ -575,6 +566,10 @@ async function cmdRun(args, config) {
|
|
|
575
566
|
skipWorktree = true;
|
|
576
567
|
i++;
|
|
577
568
|
break;
|
|
569
|
+
case "--no-attach":
|
|
570
|
+
noAttach = true;
|
|
571
|
+
i++;
|
|
572
|
+
break;
|
|
578
573
|
case "--name":
|
|
579
574
|
case "-n":
|
|
580
575
|
nameOverride = args[++i];
|
|
@@ -614,7 +609,7 @@ async function cmdRun(args, config) {
|
|
|
614
609
|
await launchAgent(input, headless, extraArgs, skipWorktree, promptFile, nameOverride, config);
|
|
615
610
|
}
|
|
616
611
|
console.log();
|
|
617
|
-
if (!headless && inputs.length === 1) {
|
|
612
|
+
if (!headless && inputs.length === 1 && !noAttach) {
|
|
618
613
|
log.info("Attaching to tmux session...");
|
|
619
614
|
log.dim(" Detach with: Ctrl-B then D");
|
|
620
615
|
console.log();
|
|
@@ -703,10 +698,11 @@ function cmdStop(args) {
|
|
|
703
698
|
function cmdResume(args, config) {
|
|
704
699
|
const id = args[0];
|
|
705
700
|
if (!id) {
|
|
706
|
-
log.error("Usage: dispatch resume <agent-id> [--headless]");
|
|
701
|
+
log.error("Usage: dispatch resume <agent-id> [--headless] [--no-attach]");
|
|
707
702
|
process.exit(1);
|
|
708
703
|
}
|
|
709
704
|
const headless = args.includes("--headless") || args.includes("-H");
|
|
705
|
+
const noAttach = args.includes("--no-attach");
|
|
710
706
|
ensureTmux();
|
|
711
707
|
const wtPath = worktreePath(id, config);
|
|
712
708
|
if (!existsSync2(wtPath)) {
|
|
@@ -725,7 +721,7 @@ function cmdResume(args, config) {
|
|
|
725
721
|
`tmux send-keys -t "${tmuxTarget(id)}" "unset CLAUDECODE && claude --continue ${modelFlag}" Enter`
|
|
726
722
|
);
|
|
727
723
|
log.ok(`Resumed agent: ${id} (interactive)`);
|
|
728
|
-
tmuxAttach();
|
|
724
|
+
if (!noAttach) tmuxAttach();
|
|
729
725
|
} else {
|
|
730
726
|
const resumePrompt = "Continue working on the task.";
|
|
731
727
|
const claudeCmd = buildClaudeCmd(
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { execSync } from "child_process";
|
|
11
|
+
import {
|
|
12
|
+
writeFileSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
readFileSync,
|
|
15
|
+
existsSync
|
|
16
|
+
} from "fs";
|
|
17
|
+
import { tmpdir } from "os";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { randomBytes } from "crypto";
|
|
20
|
+
function stripAnsi(str) {
|
|
21
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
22
|
+
}
|
|
23
|
+
function dispatch(args) {
|
|
24
|
+
const cwd = process.env.DISPATCH_CWD || process.cwd();
|
|
25
|
+
try {
|
|
26
|
+
const output = execSync(`dispatch ${args}`, {
|
|
27
|
+
encoding: "utf-8",
|
|
28
|
+
cwd,
|
|
29
|
+
timeout: 12e4,
|
|
30
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
31
|
+
});
|
|
32
|
+
return stripAnsi(output).trim();
|
|
33
|
+
} catch (e) {
|
|
34
|
+
const out = [e.stdout, e.stderr, e.message].filter(Boolean).join("\n");
|
|
35
|
+
return stripAnsi(out).trim();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function makeTempPrompt(prompt) {
|
|
39
|
+
const name = `dispatch-mcp-${randomBytes(4).toString("hex")}.md`;
|
|
40
|
+
const path = join(tmpdir(), name);
|
|
41
|
+
writeFileSync(path, prompt);
|
|
42
|
+
return path;
|
|
43
|
+
}
|
|
44
|
+
var server = new Server(
|
|
45
|
+
{ name: "dispatch", version: "0.1.0" },
|
|
46
|
+
{ capabilities: { tools: {} } }
|
|
47
|
+
);
|
|
48
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
49
|
+
tools: [
|
|
50
|
+
{
|
|
51
|
+
name: "dispatch_run",
|
|
52
|
+
description: "Launch a Claude Code agent in an isolated git worktree. Pass the full task prompt inline. Returns agent ID and branch name.",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: "object",
|
|
55
|
+
properties: {
|
|
56
|
+
prompt: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Full task description/prompt for the agent"
|
|
59
|
+
},
|
|
60
|
+
ticket: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Linear ticket ID (e.g. HEY-907). Fetches title + description if LINEAR_API_KEY is set."
|
|
63
|
+
},
|
|
64
|
+
name: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Agent name and branch name (kebab-case). Defaults to ticket ID or derived from prompt."
|
|
67
|
+
},
|
|
68
|
+
headless: {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
description: "Run in background (fire-and-forget). Default: false (interactive)."
|
|
71
|
+
},
|
|
72
|
+
model: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Claude model: sonnet, opus, haiku"
|
|
75
|
+
},
|
|
76
|
+
base_branch: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Branch to create worktree from. Default: dev."
|
|
79
|
+
},
|
|
80
|
+
max_turns: {
|
|
81
|
+
type: "number",
|
|
82
|
+
description: "Max agentic turns (headless only)"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
required: ["prompt"]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "dispatch_list",
|
|
90
|
+
description: "List all running dispatch agents with their status (running/idle/exited).",
|
|
91
|
+
inputSchema: { type: "object", properties: {} }
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "dispatch_stop",
|
|
95
|
+
description: "Stop a running dispatch agent. Worktree and branch are preserved.",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
agent_id: { type: "string", description: "Agent ID to stop" }
|
|
100
|
+
},
|
|
101
|
+
required: ["agent_id"]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "dispatch_resume",
|
|
106
|
+
description: "Resume a previously stopped agent. Picks up where it left off.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: "object",
|
|
109
|
+
properties: {
|
|
110
|
+
agent_id: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "Agent ID to resume"
|
|
113
|
+
},
|
|
114
|
+
headless: {
|
|
115
|
+
type: "boolean",
|
|
116
|
+
description: "Resume in headless mode"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
required: ["agent_id"]
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "dispatch_cleanup",
|
|
124
|
+
description: "Remove an agent's worktree and optionally its branch.",
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: {
|
|
128
|
+
agent_id: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Agent ID to clean up. Omit and set all=true for all agents."
|
|
131
|
+
},
|
|
132
|
+
all: {
|
|
133
|
+
type: "boolean",
|
|
134
|
+
description: "Clean up all worktrees"
|
|
135
|
+
},
|
|
136
|
+
delete_branch: {
|
|
137
|
+
type: "boolean",
|
|
138
|
+
description: "Also delete the git branch"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "dispatch_logs",
|
|
145
|
+
description: "Get recent output from a dispatch agent (log file or tmux capture).",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
agent_id: { type: "string", description: "Agent ID" },
|
|
150
|
+
lines: {
|
|
151
|
+
type: "number",
|
|
152
|
+
description: "Number of lines to return. Default: 50."
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
required: ["agent_id"]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}));
|
|
160
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
161
|
+
const { name, arguments: args = {} } = request.params;
|
|
162
|
+
switch (name) {
|
|
163
|
+
case "dispatch_run": {
|
|
164
|
+
const {
|
|
165
|
+
prompt,
|
|
166
|
+
ticket,
|
|
167
|
+
name: agentName,
|
|
168
|
+
headless,
|
|
169
|
+
model,
|
|
170
|
+
base_branch,
|
|
171
|
+
max_turns
|
|
172
|
+
} = args;
|
|
173
|
+
const tmpFile = makeTempPrompt(prompt);
|
|
174
|
+
try {
|
|
175
|
+
const parts = ["run"];
|
|
176
|
+
parts.push(ticket || "prompt-file");
|
|
177
|
+
parts.push(`--prompt-file "${tmpFile}"`);
|
|
178
|
+
if (agentName) parts.push(`--name "${agentName}"`);
|
|
179
|
+
if (headless) parts.push("--headless");
|
|
180
|
+
if (model) parts.push(`--model ${model}`);
|
|
181
|
+
if (base_branch) parts.push(`--base ${base_branch}`);
|
|
182
|
+
if (max_turns) parts.push(`--max-turns ${max_turns}`);
|
|
183
|
+
if (headless) parts.push("--no-attach");
|
|
184
|
+
const output = dispatch(parts.join(" "));
|
|
185
|
+
return { content: [{ type: "text", text: output }] };
|
|
186
|
+
} finally {
|
|
187
|
+
try {
|
|
188
|
+
unlinkSync(tmpFile);
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
case "dispatch_list": {
|
|
194
|
+
const output = dispatch("list");
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: output || "No agents running." }]
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
case "dispatch_stop": {
|
|
200
|
+
const { agent_id } = args;
|
|
201
|
+
const output = dispatch(`stop ${agent_id}`);
|
|
202
|
+
return { content: [{ type: "text", text: output }] };
|
|
203
|
+
}
|
|
204
|
+
case "dispatch_resume": {
|
|
205
|
+
const { agent_id, headless } = args;
|
|
206
|
+
const flags = headless ? "--headless" : "";
|
|
207
|
+
const output = dispatch(
|
|
208
|
+
`resume ${agent_id} ${flags} --no-attach`.trim()
|
|
209
|
+
);
|
|
210
|
+
return { content: [{ type: "text", text: output }] };
|
|
211
|
+
}
|
|
212
|
+
case "dispatch_cleanup": {
|
|
213
|
+
const { agent_id, all, delete_branch } = args;
|
|
214
|
+
const parts = ["cleanup"];
|
|
215
|
+
if (all) parts.push("--all");
|
|
216
|
+
else if (agent_id) parts.push(agent_id);
|
|
217
|
+
if (delete_branch) parts.push("--delete-branch");
|
|
218
|
+
const output = dispatch(parts.join(" "));
|
|
219
|
+
return { content: [{ type: "text", text: output }] };
|
|
220
|
+
}
|
|
221
|
+
case "dispatch_logs": {
|
|
222
|
+
const { agent_id, lines = 50 } = args;
|
|
223
|
+
const cwd = process.env.DISPATCH_CWD || process.cwd();
|
|
224
|
+
const logPath = join(cwd, ".worktrees", agent_id, ".dispatch.log");
|
|
225
|
+
if (existsSync(logPath)) {
|
|
226
|
+
const content = readFileSync(logPath, "utf-8");
|
|
227
|
+
const logLines = content.split("\n");
|
|
228
|
+
const lastLines = logLines.slice(-lines).join("\n");
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: "text", text: stripAnsi(lastLines) }]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const output = execSync(
|
|
235
|
+
`tmux capture-pane -t "dispatch:${agent_id}" -p -S -${lines}`,
|
|
236
|
+
{ encoding: "utf-8", timeout: 5e3 }
|
|
237
|
+
);
|
|
238
|
+
return {
|
|
239
|
+
content: [{ type: "text", text: stripAnsi(output).trim() }]
|
|
240
|
+
};
|
|
241
|
+
} catch {
|
|
242
|
+
return {
|
|
243
|
+
content: [
|
|
244
|
+
{
|
|
245
|
+
type: "text",
|
|
246
|
+
text: `Agent '${agent_id}' not found or no output available.`
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
default:
|
|
253
|
+
return {
|
|
254
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
255
|
+
isError: true
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
async function main() {
|
|
260
|
+
const transport = new StdioServerTransport();
|
|
261
|
+
await server.connect(transport);
|
|
262
|
+
}
|
|
263
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dispatch-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Orchestrate Claude Code agents in git worktrees",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"dispatch": "./dist/cli.js"
|
|
7
|
+
"dispatch": "./dist/cli.js",
|
|
8
|
+
"dispatch-mcp": "./dist/mcp.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsup",
|
|
@@ -26,6 +27,9 @@
|
|
|
26
27
|
"type": "git",
|
|
27
28
|
"url": "https://github.com/paperMoose/dispatch.git"
|
|
28
29
|
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
32
|
+
},
|
|
29
33
|
"devDependencies": {
|
|
30
34
|
"tsup": "^8.0.0",
|
|
31
35
|
"tsx": "^4.0.0",
|