claude-code-swarm 0.3.5 → 0.3.7
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.claude-plugin/run-agent-inbox-mcp.sh +22 -3
- package/.gitattributes +3 -0
- package/.opentasks/config.json +9 -0
- package/.opentasks/graph.jsonl +0 -0
- package/e2e/helpers/opentasks-daemon.mjs +149 -0
- package/e2e/tier6-live-inbox-flow.test.mjs +938 -0
- package/e2e/tier7-hooks.test.mjs +992 -0
- package/e2e/tier7-minimem.test.mjs +461 -0
- package/e2e/tier7-opentasks.test.mjs +513 -0
- package/e2e/tier7-skilltree.test.mjs +506 -0
- package/e2e/vitest.config.e2e.mjs +1 -1
- package/package.json +6 -2
- package/references/agent-inbox/package-lock.json +2 -2
- package/references/agent-inbox/package.json +1 -1
- package/references/agent-inbox/src/index.ts +16 -2
- package/references/agent-inbox/src/ipc/ipc-server.ts +58 -0
- package/references/agent-inbox/src/mcp/mcp-proxy.ts +326 -0
- package/references/agent-inbox/src/types.ts +26 -0
- package/references/agent-inbox/test/ipc-new-commands.test.ts +200 -0
- package/references/agent-inbox/test/mcp-proxy.test.ts +191 -0
- package/references/minimem/package-lock.json +2 -2
- package/references/minimem/package.json +1 -1
- package/scripts/bootstrap.mjs +8 -1
- package/scripts/map-hook.mjs +6 -2
- package/scripts/map-sidecar.mjs +19 -0
- package/scripts/team-loader.mjs +15 -8
- package/skills/swarm/SKILL.md +16 -22
- package/src/__tests__/agent-generator.test.mjs +9 -10
- package/src/__tests__/context-output.test.mjs +13 -14
- package/src/__tests__/e2e-inbox-integration.test.mjs +732 -0
- package/src/__tests__/e2e-live-inbox.test.mjs +597 -0
- package/src/__tests__/inbox-integration.test.mjs +298 -0
- package/src/__tests__/integration.test.mjs +12 -11
- package/src/__tests__/skilltree-client.test.mjs +47 -1
- package/src/agent-generator.mjs +79 -88
- package/src/bootstrap.mjs +24 -3
- package/src/context-output.mjs +238 -64
- package/src/index.mjs +2 -0
- package/src/sidecar-server.mjs +30 -0
- package/src/skilltree-client.mjs +50 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-swarm",
|
|
3
3
|
"description": "Spin up Claude Code agent teams from openteams YAML topologies with optional MAP (Multi-Agent Protocol) observability and coordination. Provides hooks for session lifecycle, agent spawn/complete tracking, and a /swarm skill to launch team configurations.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "alexngai"
|
|
7
7
|
},
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Wrapper script to run agent-inbox MCP server
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# When the sidecar's inbox socket exists, runs in proxy mode (IPC client).
|
|
4
|
+
# Otherwise falls back to standalone mode with its own storage.
|
|
5
|
+
# Exits silently if inbox is not enabled or not installed.
|
|
5
6
|
|
|
6
7
|
# Check if inbox is enabled in config
|
|
7
8
|
ENABLED=false
|
|
@@ -45,9 +46,26 @@ if [ -n "$SWARM_INBOX_SCOPE" ]; then
|
|
|
45
46
|
SCOPE="$SWARM_INBOX_SCOPE"
|
|
46
47
|
fi
|
|
47
48
|
|
|
48
|
-
# Set env vars for agent-inbox MCP mode
|
|
49
49
|
export INBOX_SCOPE="$SCOPE"
|
|
50
50
|
|
|
51
|
+
# Discover sidecar inbox socket for proxy mode
|
|
52
|
+
# Check well-known paths: .swarm/claude-swarm/tmp/map/inbox.sock
|
|
53
|
+
INBOX_SOCK=""
|
|
54
|
+
if [ -S .swarm/claude-swarm/tmp/map/inbox.sock ]; then
|
|
55
|
+
INBOX_SOCK=".swarm/claude-swarm/tmp/map/inbox.sock"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Also check per-session paths
|
|
59
|
+
if [ -z "$INBOX_SOCK" ] && [ -d .swarm/claude-swarm/tmp/map/sessions ]; then
|
|
60
|
+
# Find the most recently modified inbox.sock in session dirs
|
|
61
|
+
INBOX_SOCK=$(find .swarm/claude-swarm/tmp/map/sessions -name inbox.sock -type s 2>/dev/null | head -1)
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# If inbox socket found, enable proxy mode
|
|
65
|
+
if [ -n "$INBOX_SOCK" ]; then
|
|
66
|
+
export INBOX_SOCKET_PATH="$INBOX_SOCK"
|
|
67
|
+
fi
|
|
68
|
+
|
|
51
69
|
# Try to find the agent-inbox module entry point
|
|
52
70
|
INBOX_MAIN=""
|
|
53
71
|
|
|
@@ -68,6 +86,7 @@ if [ -z "$INBOX_MAIN" ]; then
|
|
|
68
86
|
fi
|
|
69
87
|
|
|
70
88
|
if [ -n "$INBOX_MAIN" ]; then
|
|
89
|
+
# Uses proxy mode when INBOX_SOCKET_PATH is set, standalone otherwise
|
|
71
90
|
exec node "$INBOX_MAIN" mcp
|
|
72
91
|
fi
|
|
73
92
|
|
package/.gitattributes
ADDED
|
File without changes
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helper: minimal opentasks daemon for e2e tests
|
|
3
|
+
*
|
|
4
|
+
* Creates a JSON-RPC 2.0 server on a Unix socket that implements the
|
|
5
|
+
* subset of opentasks daemon methods used by opentasks-client.mjs:
|
|
6
|
+
* - ping → { pong: true }
|
|
7
|
+
* - graph.create → stores node, returns { id, ...params }
|
|
8
|
+
* - graph.update → updates stored node, returns updated node
|
|
9
|
+
* - tools.link → stores edge, returns { ok: true }
|
|
10
|
+
*
|
|
11
|
+
* Uses in-memory storage. No persistence, no providers, no flush.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import net from "net";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import { randomUUID } from "crypto";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Start a minimal opentasks daemon on a Unix socket.
|
|
20
|
+
* Returns { socketPath, stop(), nodes, edges }.
|
|
21
|
+
*/
|
|
22
|
+
export function startTestDaemon(socketPath) {
|
|
23
|
+
const nodes = new Map();
|
|
24
|
+
const edges = [];
|
|
25
|
+
let server;
|
|
26
|
+
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
// Remove stale socket
|
|
29
|
+
try { fs.unlinkSync(socketPath); } catch { /* ignore */ }
|
|
30
|
+
|
|
31
|
+
server = net.createServer((conn) => {
|
|
32
|
+
let buffer = "";
|
|
33
|
+
|
|
34
|
+
conn.on("data", (data) => {
|
|
35
|
+
buffer += data.toString();
|
|
36
|
+
const lines = buffer.split("\n");
|
|
37
|
+
buffer = lines.pop(); // Keep incomplete line
|
|
38
|
+
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
if (!line.trim()) continue;
|
|
41
|
+
try {
|
|
42
|
+
const req = JSON.parse(line);
|
|
43
|
+
const result = handleRequest(req, nodes, edges);
|
|
44
|
+
conn.write(JSON.stringify(result) + "\n");
|
|
45
|
+
} catch (err) {
|
|
46
|
+
conn.write(JSON.stringify({
|
|
47
|
+
jsonrpc: "2.0",
|
|
48
|
+
id: null,
|
|
49
|
+
error: { code: -32700, message: "Parse error", data: err.message },
|
|
50
|
+
}) + "\n");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
server.on("error", reject);
|
|
57
|
+
|
|
58
|
+
server.listen(socketPath, () => {
|
|
59
|
+
resolve({
|
|
60
|
+
socketPath,
|
|
61
|
+
nodes,
|
|
62
|
+
edges,
|
|
63
|
+
stop: () => new Promise((res) => {
|
|
64
|
+
server.close(() => {
|
|
65
|
+
try { fs.unlinkSync(socketPath); } catch { /* ignore */ }
|
|
66
|
+
res();
|
|
67
|
+
});
|
|
68
|
+
// Force close connections
|
|
69
|
+
server.unref();
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function handleRequest(req, nodes, edges) {
|
|
77
|
+
const { id, method, params } = req;
|
|
78
|
+
|
|
79
|
+
switch (method) {
|
|
80
|
+
case "ping":
|
|
81
|
+
return { jsonrpc: "2.0", id, result: { pong: true } };
|
|
82
|
+
|
|
83
|
+
case "graph.create": {
|
|
84
|
+
const nodeId = randomUUID();
|
|
85
|
+
const node = {
|
|
86
|
+
id: nodeId,
|
|
87
|
+
type: params?.type || "task",
|
|
88
|
+
title: params?.title || "",
|
|
89
|
+
status: params?.status || "open",
|
|
90
|
+
assignee: params?.assignee || null,
|
|
91
|
+
uri: params?.uri || null,
|
|
92
|
+
metadata: params?.metadata || {},
|
|
93
|
+
createdAt: new Date().toISOString(),
|
|
94
|
+
updatedAt: new Date().toISOString(),
|
|
95
|
+
};
|
|
96
|
+
nodes.set(nodeId, node);
|
|
97
|
+
return { jsonrpc: "2.0", id, result: node };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case "graph.update": {
|
|
101
|
+
const nodeId = params?.id;
|
|
102
|
+
if (!nodeId || !nodes.has(nodeId)) {
|
|
103
|
+
return {
|
|
104
|
+
jsonrpc: "2.0", id,
|
|
105
|
+
error: { code: -32602, message: `Node not found: ${nodeId}` },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const existing = nodes.get(nodeId);
|
|
109
|
+
const updated = {
|
|
110
|
+
...existing,
|
|
111
|
+
...(params.status !== undefined && { status: params.status }),
|
|
112
|
+
...(params.title !== undefined && { title: params.title }),
|
|
113
|
+
...(params.assignee !== undefined && { assignee: params.assignee }),
|
|
114
|
+
...(params.metadata !== undefined && { metadata: { ...existing.metadata, ...params.metadata } }),
|
|
115
|
+
updatedAt: new Date().toISOString(),
|
|
116
|
+
};
|
|
117
|
+
nodes.set(nodeId, updated);
|
|
118
|
+
return { jsonrpc: "2.0", id, result: updated };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case "graph.query": {
|
|
122
|
+
const allNodes = Array.from(nodes.values());
|
|
123
|
+
const filtered = params?.type
|
|
124
|
+
? allNodes.filter((n) => n.type === params.type)
|
|
125
|
+
: allNodes;
|
|
126
|
+
return { jsonrpc: "2.0", id, result: filtered };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
case "tools.link": {
|
|
130
|
+
const edge = {
|
|
131
|
+
fromId: params?.fromId,
|
|
132
|
+
toId: params?.toId,
|
|
133
|
+
type: params?.type || "related",
|
|
134
|
+
metadata: params?.metadata || {},
|
|
135
|
+
};
|
|
136
|
+
edges.push(edge);
|
|
137
|
+
return { jsonrpc: "2.0", id, result: { ok: true } };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case "shutdown":
|
|
141
|
+
return { jsonrpc: "2.0", id, result: { ok: true } };
|
|
142
|
+
|
|
143
|
+
default:
|
|
144
|
+
return {
|
|
145
|
+
jsonrpc: "2.0", id,
|
|
146
|
+
error: { code: -32601, message: `Method not found: ${method}` },
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|