opencode-swarm-plugin 0.11.3 → 0.12.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/.beads/issues.jsonl +6 -0
- package/bin/swarm.ts +188 -2
- package/dist/index.js +52 -1
- package/dist/plugin.js +51 -1
- package/examples/plugin-wrapper-template.ts +723 -0
- package/package.json +1 -1
- package/src/agent-mail.ts +106 -3
- package/src/index.ts +22 -0
package/.beads/issues.jsonl
CHANGED
|
@@ -678,6 +678,12 @@
|
|
|
678
678
|
{"id":"opencode-swarm-plugin-fmkz8.1","title":"Step 1","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-08T11:12:43.192563-08:00","updated_at":"2025-12-08T11:12:43.285206-08:00","closed_at":"2025-12-08T11:12:43.285206-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fmkz8.1","depends_on_id":"opencode-swarm-plugin-fmkz8","type":"parent-child","created_at":"2025-12-08T11:12:43.192901-08:00","created_by":"daemon"}]}
|
|
679
679
|
{"id":"opencode-swarm-plugin-fmkz8.2","title":"Step 2","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-08T11:12:43.229469-08:00","updated_at":"2025-12-08T11:12:43.340511-08:00","closed_at":"2025-12-08T11:12:43.340511-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fmkz8.2","depends_on_id":"opencode-swarm-plugin-fmkz8","type":"parent-child","created_at":"2025-12-08T11:12:43.229808-08:00","created_by":"daemon"}]}
|
|
680
680
|
{"id":"opencode-swarm-plugin-fmte","title":"Thread link test bead","description":"[thread:test-thread-456]","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-08T08:14:30.118389-08:00","updated_at":"2025-12-08T08:14:31.757071-08:00","closed_at":"2025-12-08T08:14:31.757071-08:00"}
|
|
681
|
+
{"id":"opencode-swarm-plugin-fn2a3","title":"CLI-based plugin wrapper for OpenCode","description":"Replace broken npm import plugin with thin wrapper that shells out to `swarm tool` CLI. Fixes bun: protocol crash.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-09T12:44:59.286448-08:00","updated_at":"2025-12-09T12:53:22.927803-08:00","closed_at":"2025-12-09T12:53:22.927803-08:00"}
|
|
682
|
+
{"id":"opencode-swarm-plugin-fn2a3.1","title":"Add CLI tool subcommand with JSON interface","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-09T12:44:59.380582-08:00","updated_at":"2025-12-09T12:48:28.655484-08:00","closed_at":"2025-12-09T12:48:28.655484-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fn2a3.1","depends_on_id":"opencode-swarm-plugin-fn2a3","type":"parent-child","created_at":"2025-12-09T12:44:59.381424-08:00","created_by":"daemon"}]}
|
|
683
|
+
{"id":"opencode-swarm-plugin-fn2a3.2","title":"Add file-based session state for Agent Mail","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-09T12:44:59.452126-08:00","updated_at":"2025-12-09T12:50:05.569927-08:00","closed_at":"2025-12-09T12:50:05.569927-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fn2a3.2","depends_on_id":"opencode-swarm-plugin-fn2a3","type":"parent-child","created_at":"2025-12-09T12:44:59.452571-08:00","created_by":"daemon"}]}
|
|
684
|
+
{"id":"opencode-swarm-plugin-fn2a3.3","title":"Create plugin wrapper template generator","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-09T12:44:59.511026-08:00","updated_at":"2025-12-09T12:52:13.47915-08:00","closed_at":"2025-12-09T12:52:13.47915-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fn2a3.3","depends_on_id":"opencode-swarm-plugin-fn2a3","type":"parent-child","created_at":"2025-12-09T12:44:59.511712-08:00","created_by":"daemon"}]}
|
|
685
|
+
{"id":"opencode-swarm-plugin-fn2a3.4","title":"Update setup command to generate new plugin wrapper","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-09T12:44:59.559682-08:00","updated_at":"2025-12-09T12:53:13.282356-08:00","closed_at":"2025-12-09T12:53:13.282356-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fn2a3.4","depends_on_id":"opencode-swarm-plugin-fn2a3","type":"parent-child","created_at":"2025-12-09T12:44:59.560036-08:00","created_by":"daemon"}]}
|
|
686
|
+
{"id":"opencode-swarm-plugin-fn2a3.5","title":"Export tool registry from source modules","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-09T12:44:59.609125-08:00","updated_at":"2025-12-09T12:46:18.092813-08:00","closed_at":"2025-12-09T12:46:18.092813-08:00","dependencies":[{"issue_id":"opencode-swarm-plugin-fn2a3.5","depends_on_id":"opencode-swarm-plugin-fn2a3","type":"parent-child","created_at":"2025-12-09T12:44:59.609492-08:00","created_by":"daemon"}]}
|
|
681
687
|
{"id":"opencode-swarm-plugin-fo9z","title":"Query test bead","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-08T07:53:43.635138-08:00","updated_at":"2025-12-08T07:53:46.190181-08:00","closed_at":"2025-12-08T07:53:46.190181-08:00"}
|
|
682
688
|
{"id":"opencode-swarm-plugin-foht","title":"New feature request","description":"","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-08T11:11:07.341455-08:00","updated_at":"2025-12-08T11:11:10.086863-08:00","closed_at":"2025-12-08T11:11:10.086863-08:00"}
|
|
683
689
|
{"id":"opencode-swarm-plugin-fomf","title":"Limit test bead 1","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-08T11:12:25.714274-08:00","updated_at":"2025-12-08T11:12:28.097683-08:00","closed_at":"2025-12-08T11:12:28.097683-08:00"}
|
package/bin/swarm.ts
CHANGED
|
@@ -457,9 +457,32 @@ async function checkAllDependencies(): Promise<CheckResult[]> {
|
|
|
457
457
|
// File Templates
|
|
458
458
|
// ============================================================================
|
|
459
459
|
|
|
460
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Get the plugin wrapper template
|
|
462
|
+
*
|
|
463
|
+
* Reads from examples/plugin-wrapper-template.ts which contains a self-contained
|
|
464
|
+
* plugin that shells out to the `swarm` CLI for all tool execution.
|
|
465
|
+
*/
|
|
466
|
+
function getPluginWrapper(): string {
|
|
467
|
+
const templatePath = join(
|
|
468
|
+
__dirname,
|
|
469
|
+
"..",
|
|
470
|
+
"examples",
|
|
471
|
+
"plugin-wrapper-template.ts",
|
|
472
|
+
);
|
|
473
|
+
try {
|
|
474
|
+
return readFileSync(templatePath, "utf-8");
|
|
475
|
+
} catch (error) {
|
|
476
|
+
// Fallback to minimal wrapper if template not found (shouldn't happen in normal install)
|
|
477
|
+
console.warn(
|
|
478
|
+
`[swarm] Could not read plugin template from ${templatePath}, using minimal wrapper`,
|
|
479
|
+
);
|
|
480
|
+
return `// Minimal fallback - install opencode-swarm-plugin globally for full functionality
|
|
481
|
+
import { SwarmPlugin } from "opencode-swarm-plugin"
|
|
461
482
|
export default SwarmPlugin
|
|
462
483
|
`;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
463
486
|
|
|
464
487
|
const SWARM_COMMAND = `---
|
|
465
488
|
description: Decompose task into parallel subtasks and coordinate agents
|
|
@@ -961,7 +984,7 @@ async function setup() {
|
|
|
961
984
|
}
|
|
962
985
|
}
|
|
963
986
|
|
|
964
|
-
writeFileSync(pluginPath,
|
|
987
|
+
writeFileSync(pluginPath, getPluginWrapper());
|
|
965
988
|
p.log.success("Plugin: " + pluginPath);
|
|
966
989
|
|
|
967
990
|
writeFileSync(commandPath, SWARM_COMMAND);
|
|
@@ -1180,8 +1203,14 @@ ${cyan("Commands:")}
|
|
|
1180
1203
|
swarm config Show paths to generated config files
|
|
1181
1204
|
swarm update Update to latest version
|
|
1182
1205
|
swarm version Show version and banner
|
|
1206
|
+
swarm tool Execute a tool (for plugin wrapper)
|
|
1183
1207
|
swarm help Show this help
|
|
1184
1208
|
|
|
1209
|
+
${cyan("Tool Execution:")}
|
|
1210
|
+
swarm tool --list List all available tools
|
|
1211
|
+
swarm tool <name> Execute tool with no args
|
|
1212
|
+
swarm tool <name> --json '<args>' Execute tool with JSON args
|
|
1213
|
+
|
|
1185
1214
|
${cyan("Usage in OpenCode:")}
|
|
1186
1215
|
/swarm "Add user authentication with OAuth"
|
|
1187
1216
|
@swarm-planner "Decompose this into parallel tasks"
|
|
@@ -1202,6 +1231,150 @@ ${dim("Docs: https://github.com/joelhooks/opencode-swarm-plugin")}
|
|
|
1202
1231
|
if (updateInfo) showUpdateNotification(updateInfo);
|
|
1203
1232
|
}
|
|
1204
1233
|
|
|
1234
|
+
// ============================================================================
|
|
1235
|
+
// Tool Execution (for plugin wrapper)
|
|
1236
|
+
// ============================================================================
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Execute a tool by name with JSON args
|
|
1240
|
+
*
|
|
1241
|
+
* This is the bridge between the plugin wrapper and the actual tool implementations.
|
|
1242
|
+
* The plugin wrapper shells out to `swarm tool <name> --json '<args>'` and this
|
|
1243
|
+
* function executes the tool and returns JSON.
|
|
1244
|
+
*
|
|
1245
|
+
* Exit codes:
|
|
1246
|
+
* - 0: Success
|
|
1247
|
+
* - 1: Tool execution error (error details in JSON output)
|
|
1248
|
+
* - 2: Unknown tool name
|
|
1249
|
+
* - 3: Invalid JSON args
|
|
1250
|
+
*/
|
|
1251
|
+
async function executeTool(toolName: string, argsJson?: string) {
|
|
1252
|
+
// Lazy import to avoid loading all tools on every CLI invocation
|
|
1253
|
+
const { allTools } = await import("../src/index");
|
|
1254
|
+
|
|
1255
|
+
// Validate tool name
|
|
1256
|
+
if (!(toolName in allTools)) {
|
|
1257
|
+
const availableTools = Object.keys(allTools).sort();
|
|
1258
|
+
console.log(
|
|
1259
|
+
JSON.stringify({
|
|
1260
|
+
success: false,
|
|
1261
|
+
error: {
|
|
1262
|
+
code: "UNKNOWN_TOOL",
|
|
1263
|
+
message: `Unknown tool: ${toolName}`,
|
|
1264
|
+
available_tools: availableTools,
|
|
1265
|
+
},
|
|
1266
|
+
}),
|
|
1267
|
+
);
|
|
1268
|
+
process.exit(2);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Parse args
|
|
1272
|
+
let args: Record<string, unknown> = {};
|
|
1273
|
+
if (argsJson) {
|
|
1274
|
+
try {
|
|
1275
|
+
args = JSON.parse(argsJson);
|
|
1276
|
+
} catch (e) {
|
|
1277
|
+
console.log(
|
|
1278
|
+
JSON.stringify({
|
|
1279
|
+
success: false,
|
|
1280
|
+
error: {
|
|
1281
|
+
code: "INVALID_JSON",
|
|
1282
|
+
message: `Invalid JSON args: ${e instanceof Error ? e.message : String(e)}`,
|
|
1283
|
+
raw_input: argsJson.slice(0, 200),
|
|
1284
|
+
},
|
|
1285
|
+
}),
|
|
1286
|
+
);
|
|
1287
|
+
process.exit(3);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Create mock context for tools that need sessionID
|
|
1292
|
+
// This mimics what OpenCode provides to plugins
|
|
1293
|
+
const mockContext = {
|
|
1294
|
+
sessionID: process.env.OPENCODE_SESSION_ID || `cli-${Date.now()}`,
|
|
1295
|
+
messageID: process.env.OPENCODE_MESSAGE_ID || `msg-${Date.now()}`,
|
|
1296
|
+
agent: process.env.OPENCODE_AGENT || "cli",
|
|
1297
|
+
abort: new AbortController().signal,
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
// Get the tool
|
|
1301
|
+
const toolDef = allTools[toolName as keyof typeof allTools];
|
|
1302
|
+
|
|
1303
|
+
// Execute tool
|
|
1304
|
+
// Note: We cast args to any because the CLI accepts arbitrary JSON
|
|
1305
|
+
// The tool's internal Zod validation will catch type errors
|
|
1306
|
+
try {
|
|
1307
|
+
const result = await toolDef.execute(args as any, mockContext);
|
|
1308
|
+
|
|
1309
|
+
// If result is already valid JSON, try to parse and re-wrap it
|
|
1310
|
+
// Otherwise wrap the string result
|
|
1311
|
+
try {
|
|
1312
|
+
const parsed = JSON.parse(result);
|
|
1313
|
+
// If it's already a success/error response, pass through
|
|
1314
|
+
if (typeof parsed === "object" && "success" in parsed) {
|
|
1315
|
+
console.log(JSON.stringify(parsed));
|
|
1316
|
+
} else {
|
|
1317
|
+
console.log(JSON.stringify({ success: true, data: parsed }));
|
|
1318
|
+
}
|
|
1319
|
+
} catch {
|
|
1320
|
+
// Result is a plain string, wrap it
|
|
1321
|
+
console.log(JSON.stringify({ success: true, data: result }));
|
|
1322
|
+
}
|
|
1323
|
+
process.exit(0);
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
console.log(
|
|
1326
|
+
JSON.stringify({
|
|
1327
|
+
success: false,
|
|
1328
|
+
error: {
|
|
1329
|
+
code: error instanceof Error ? error.name : "TOOL_ERROR",
|
|
1330
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1331
|
+
details:
|
|
1332
|
+
error instanceof Error && "zodError" in error
|
|
1333
|
+
? (error as { zodError?: unknown }).zodError
|
|
1334
|
+
: undefined,
|
|
1335
|
+
},
|
|
1336
|
+
}),
|
|
1337
|
+
);
|
|
1338
|
+
process.exit(1);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/**
|
|
1343
|
+
* List all available tools
|
|
1344
|
+
*/
|
|
1345
|
+
async function listTools() {
|
|
1346
|
+
const { allTools } = await import("../src/index");
|
|
1347
|
+
const tools = Object.keys(allTools).sort();
|
|
1348
|
+
|
|
1349
|
+
console.log(yellow(BANNER));
|
|
1350
|
+
console.log(dim(" " + TAGLINE + " v" + VERSION));
|
|
1351
|
+
console.log();
|
|
1352
|
+
console.log(cyan("Available tools:") + ` (${tools.length} total)`);
|
|
1353
|
+
console.log();
|
|
1354
|
+
|
|
1355
|
+
// Group by prefix
|
|
1356
|
+
const groups: Record<string, string[]> = {};
|
|
1357
|
+
for (const tool of tools) {
|
|
1358
|
+
const prefix = tool.split("_")[0];
|
|
1359
|
+
if (!groups[prefix]) groups[prefix] = [];
|
|
1360
|
+
groups[prefix].push(tool);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
for (const [prefix, toolList] of Object.entries(groups)) {
|
|
1364
|
+
console.log(green(` ${prefix}:`));
|
|
1365
|
+
for (const t of toolList) {
|
|
1366
|
+
console.log(` ${t}`);
|
|
1367
|
+
}
|
|
1368
|
+
console.log();
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
console.log(dim("Usage: swarm tool <name> [--json '<args>']"));
|
|
1372
|
+
console.log(dim("Example: swarm tool beads_ready"));
|
|
1373
|
+
console.log(
|
|
1374
|
+
dim('Example: swarm tool beads_create --json \'{"title": "Fix bug"}\''),
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1205
1378
|
// ============================================================================
|
|
1206
1379
|
// Main
|
|
1207
1380
|
// ============================================================================
|
|
@@ -1224,6 +1397,19 @@ switch (command) {
|
|
|
1224
1397
|
case "update":
|
|
1225
1398
|
await update();
|
|
1226
1399
|
break;
|
|
1400
|
+
case "tool": {
|
|
1401
|
+
const toolName = process.argv[3];
|
|
1402
|
+
if (!toolName || toolName === "--list" || toolName === "-l") {
|
|
1403
|
+
await listTools();
|
|
1404
|
+
} else {
|
|
1405
|
+
// Look for --json flag
|
|
1406
|
+
const jsonFlagIndex = process.argv.indexOf("--json");
|
|
1407
|
+
const argsJson =
|
|
1408
|
+
jsonFlagIndex !== -1 ? process.argv[jsonFlagIndex + 1] : undefined;
|
|
1409
|
+
await executeTool(toolName, argsJson);
|
|
1410
|
+
}
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1227
1413
|
case "version":
|
|
1228
1414
|
case "--version":
|
|
1229
1415
|
case "-v":
|
package/dist/index.js
CHANGED
|
@@ -22590,6 +22590,15 @@ async function getRateLimiter() {
|
|
|
22590
22590
|
}
|
|
22591
22591
|
|
|
22592
22592
|
// src/agent-mail.ts
|
|
22593
|
+
import {
|
|
22594
|
+
existsSync as existsSync2,
|
|
22595
|
+
mkdirSync as mkdirSync2,
|
|
22596
|
+
readFileSync,
|
|
22597
|
+
writeFileSync,
|
|
22598
|
+
unlinkSync
|
|
22599
|
+
} from "fs";
|
|
22600
|
+
import { join as join2 } from "path";
|
|
22601
|
+
import { tmpdir } from "os";
|
|
22593
22602
|
var AGENT_MAIL_URL = "http://127.0.0.1:8765";
|
|
22594
22603
|
var DEFAULT_TTL_SECONDS = 3600;
|
|
22595
22604
|
var MAX_INBOX_LIMIT = 5;
|
|
@@ -22605,6 +22614,34 @@ var RECOVERY_CONFIG = {
|
|
|
22605
22614
|
restartCooldownMs: 30000,
|
|
22606
22615
|
enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
|
|
22607
22616
|
};
|
|
22617
|
+
var SESSION_STATE_DIR = process.env.SWARM_STATE_DIR || join2(tmpdir(), "swarm-sessions");
|
|
22618
|
+
function getSessionStatePath(sessionID) {
|
|
22619
|
+
const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
22620
|
+
return join2(SESSION_STATE_DIR, `${safeID}.json`);
|
|
22621
|
+
}
|
|
22622
|
+
function loadSessionState(sessionID) {
|
|
22623
|
+
const path = getSessionStatePath(sessionID);
|
|
22624
|
+
try {
|
|
22625
|
+
if (existsSync2(path)) {
|
|
22626
|
+
const data = readFileSync(path, "utf-8");
|
|
22627
|
+
return JSON.parse(data);
|
|
22628
|
+
}
|
|
22629
|
+
} catch (error45) {
|
|
22630
|
+
console.warn(`[agent-mail] Could not load session state: ${error45}`);
|
|
22631
|
+
}
|
|
22632
|
+
return null;
|
|
22633
|
+
}
|
|
22634
|
+
function saveSessionState(sessionID, state) {
|
|
22635
|
+
try {
|
|
22636
|
+
if (!existsSync2(SESSION_STATE_DIR)) {
|
|
22637
|
+
mkdirSync2(SESSION_STATE_DIR, { recursive: true });
|
|
22638
|
+
}
|
|
22639
|
+
const path = getSessionStatePath(sessionID);
|
|
22640
|
+
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
22641
|
+
} catch (error45) {
|
|
22642
|
+
console.warn(`[agent-mail] Could not save session state: ${error45}`);
|
|
22643
|
+
}
|
|
22644
|
+
}
|
|
22608
22645
|
var sessionStates = new Map;
|
|
22609
22646
|
|
|
22610
22647
|
class AgentMailError extends Error {
|
|
@@ -22908,7 +22945,13 @@ async function mcpCall(toolName, args) {
|
|
|
22908
22945
|
throw lastError || new Error("Unknown error in mcpCall");
|
|
22909
22946
|
}
|
|
22910
22947
|
function requireState(sessionID) {
|
|
22911
|
-
|
|
22948
|
+
let state = sessionStates.get(sessionID);
|
|
22949
|
+
if (!state) {
|
|
22950
|
+
state = loadSessionState(sessionID) ?? undefined;
|
|
22951
|
+
if (state) {
|
|
22952
|
+
sessionStates.set(sessionID, state);
|
|
22953
|
+
}
|
|
22954
|
+
}
|
|
22912
22955
|
if (!state) {
|
|
22913
22956
|
throw new AgentMailNotInitializedError;
|
|
22914
22957
|
}
|
|
@@ -22916,6 +22959,7 @@ function requireState(sessionID) {
|
|
|
22916
22959
|
}
|
|
22917
22960
|
function setState(sessionID, state) {
|
|
22918
22961
|
sessionStates.set(sessionID, state);
|
|
22962
|
+
saveSessionState(sessionID, state);
|
|
22919
22963
|
}
|
|
22920
22964
|
var agentmail_init = tool({
|
|
22921
22965
|
description: "Initialize Agent Mail session (ensure project + register agent)",
|
|
@@ -25554,6 +25598,12 @@ var SwarmPlugin = async (input) => {
|
|
|
25554
25598
|
};
|
|
25555
25599
|
};
|
|
25556
25600
|
var src_default = SwarmPlugin;
|
|
25601
|
+
var allTools = {
|
|
25602
|
+
...beadsTools,
|
|
25603
|
+
...agentMailTools,
|
|
25604
|
+
...structuredTools,
|
|
25605
|
+
...swarmTools
|
|
25606
|
+
};
|
|
25557
25607
|
export {
|
|
25558
25608
|
withToolFallback,
|
|
25559
25609
|
warnMissingTool,
|
|
@@ -25592,6 +25642,7 @@ export {
|
|
|
25592
25642
|
beads_create,
|
|
25593
25643
|
beads_close,
|
|
25594
25644
|
beadsTools,
|
|
25645
|
+
allTools,
|
|
25595
25646
|
agentMailTools,
|
|
25596
25647
|
WeightedEvaluationSchema,
|
|
25597
25648
|
WeightedCriterionEvaluationSchema,
|
package/dist/plugin.js
CHANGED
|
@@ -22564,6 +22564,15 @@ async function getRateLimiter() {
|
|
|
22564
22564
|
}
|
|
22565
22565
|
|
|
22566
22566
|
// src/agent-mail.ts
|
|
22567
|
+
import {
|
|
22568
|
+
existsSync as existsSync2,
|
|
22569
|
+
mkdirSync as mkdirSync2,
|
|
22570
|
+
readFileSync,
|
|
22571
|
+
writeFileSync,
|
|
22572
|
+
unlinkSync
|
|
22573
|
+
} from "fs";
|
|
22574
|
+
import { join as join2 } from "path";
|
|
22575
|
+
import { tmpdir } from "os";
|
|
22567
22576
|
var AGENT_MAIL_URL = "http://127.0.0.1:8765";
|
|
22568
22577
|
var DEFAULT_TTL_SECONDS = 3600;
|
|
22569
22578
|
var MAX_INBOX_LIMIT = 5;
|
|
@@ -22579,6 +22588,34 @@ var RECOVERY_CONFIG = {
|
|
|
22579
22588
|
restartCooldownMs: 30000,
|
|
22580
22589
|
enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
|
|
22581
22590
|
};
|
|
22591
|
+
var SESSION_STATE_DIR = process.env.SWARM_STATE_DIR || join2(tmpdir(), "swarm-sessions");
|
|
22592
|
+
function getSessionStatePath(sessionID) {
|
|
22593
|
+
const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
22594
|
+
return join2(SESSION_STATE_DIR, `${safeID}.json`);
|
|
22595
|
+
}
|
|
22596
|
+
function loadSessionState(sessionID) {
|
|
22597
|
+
const path = getSessionStatePath(sessionID);
|
|
22598
|
+
try {
|
|
22599
|
+
if (existsSync2(path)) {
|
|
22600
|
+
const data = readFileSync(path, "utf-8");
|
|
22601
|
+
return JSON.parse(data);
|
|
22602
|
+
}
|
|
22603
|
+
} catch (error45) {
|
|
22604
|
+
console.warn(`[agent-mail] Could not load session state: ${error45}`);
|
|
22605
|
+
}
|
|
22606
|
+
return null;
|
|
22607
|
+
}
|
|
22608
|
+
function saveSessionState(sessionID, state) {
|
|
22609
|
+
try {
|
|
22610
|
+
if (!existsSync2(SESSION_STATE_DIR)) {
|
|
22611
|
+
mkdirSync2(SESSION_STATE_DIR, { recursive: true });
|
|
22612
|
+
}
|
|
22613
|
+
const path = getSessionStatePath(sessionID);
|
|
22614
|
+
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
22615
|
+
} catch (error45) {
|
|
22616
|
+
console.warn(`[agent-mail] Could not save session state: ${error45}`);
|
|
22617
|
+
}
|
|
22618
|
+
}
|
|
22582
22619
|
var sessionStates = new Map;
|
|
22583
22620
|
|
|
22584
22621
|
class AgentMailError extends Error {
|
|
@@ -22882,7 +22919,13 @@ async function mcpCall(toolName, args) {
|
|
|
22882
22919
|
throw lastError || new Error("Unknown error in mcpCall");
|
|
22883
22920
|
}
|
|
22884
22921
|
function requireState(sessionID) {
|
|
22885
|
-
|
|
22922
|
+
let state = sessionStates.get(sessionID);
|
|
22923
|
+
if (!state) {
|
|
22924
|
+
state = loadSessionState(sessionID) ?? undefined;
|
|
22925
|
+
if (state) {
|
|
22926
|
+
sessionStates.set(sessionID, state);
|
|
22927
|
+
}
|
|
22928
|
+
}
|
|
22886
22929
|
if (!state) {
|
|
22887
22930
|
throw new AgentMailNotInitializedError;
|
|
22888
22931
|
}
|
|
@@ -22890,6 +22933,7 @@ function requireState(sessionID) {
|
|
|
22890
22933
|
}
|
|
22891
22934
|
function setState(sessionID, state) {
|
|
22892
22935
|
sessionStates.set(sessionID, state);
|
|
22936
|
+
saveSessionState(sessionID, state);
|
|
22893
22937
|
}
|
|
22894
22938
|
var agentmail_init = tool({
|
|
22895
22939
|
description: "Initialize Agent Mail session (ensure project + register agent)",
|
|
@@ -25203,6 +25247,12 @@ var SwarmPlugin = async (input) => {
|
|
|
25203
25247
|
}
|
|
25204
25248
|
};
|
|
25205
25249
|
};
|
|
25250
|
+
var allTools = {
|
|
25251
|
+
...beadsTools,
|
|
25252
|
+
...agentMailTools,
|
|
25253
|
+
...structuredTools,
|
|
25254
|
+
...swarmTools
|
|
25255
|
+
};
|
|
25206
25256
|
export {
|
|
25207
25257
|
SwarmPlugin
|
|
25208
25258
|
};
|
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Swarm Plugin Wrapper
|
|
3
|
+
*
|
|
4
|
+
* This is a thin wrapper that shells out to the `swarm` CLI for all tool execution.
|
|
5
|
+
* Generated by: swarm setup
|
|
6
|
+
*
|
|
7
|
+
* The plugin only depends on @opencode-ai/plugin (provided by OpenCode).
|
|
8
|
+
* All tool logic lives in the npm package - this just bridges to it.
|
|
9
|
+
*
|
|
10
|
+
* Environment variables:
|
|
11
|
+
* - OPENCODE_SESSION_ID: Passed to CLI for session state persistence
|
|
12
|
+
* - OPENCODE_MESSAGE_ID: Passed to CLI for context
|
|
13
|
+
* - OPENCODE_AGENT: Passed to CLI for context
|
|
14
|
+
*/
|
|
15
|
+
import type { Plugin, PluginInput, Hooks } from "@opencode-ai/plugin";
|
|
16
|
+
import { tool } from "@opencode-ai/plugin";
|
|
17
|
+
import { spawn } from "child_process";
|
|
18
|
+
|
|
19
|
+
const SWARM_CLI = "swarm";
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// CLI Execution Helper
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Execute a swarm tool via CLI
|
|
27
|
+
*
|
|
28
|
+
* Spawns `swarm tool <name> --json '<args>'` and returns the result.
|
|
29
|
+
* Passes session context via environment variables.
|
|
30
|
+
*/
|
|
31
|
+
async function execTool(
|
|
32
|
+
name: string,
|
|
33
|
+
args: Record<string, unknown>,
|
|
34
|
+
ctx: { sessionID: string; messageID: string; agent: string },
|
|
35
|
+
): Promise<string> {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
const hasArgs = Object.keys(args).length > 0;
|
|
38
|
+
const cliArgs = hasArgs
|
|
39
|
+
? ["tool", name, "--json", JSON.stringify(args)]
|
|
40
|
+
: ["tool", name];
|
|
41
|
+
|
|
42
|
+
const proc = spawn(SWARM_CLI, cliArgs, {
|
|
43
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
OPENCODE_SESSION_ID: ctx.sessionID,
|
|
47
|
+
OPENCODE_MESSAGE_ID: ctx.messageID,
|
|
48
|
+
OPENCODE_AGENT: ctx.agent,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
let stdout = "";
|
|
53
|
+
let stderr = "";
|
|
54
|
+
|
|
55
|
+
proc.stdout.on("data", (data) => {
|
|
56
|
+
stdout += data;
|
|
57
|
+
});
|
|
58
|
+
proc.stderr.on("data", (data) => {
|
|
59
|
+
stderr += data;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
proc.on("close", (code) => {
|
|
63
|
+
if (code === 0) {
|
|
64
|
+
// Success - return the JSON output
|
|
65
|
+
try {
|
|
66
|
+
const result = JSON.parse(stdout);
|
|
67
|
+
if (result.success && result.data !== undefined) {
|
|
68
|
+
// Unwrap the data for cleaner tool output
|
|
69
|
+
resolve(
|
|
70
|
+
typeof result.data === "string"
|
|
71
|
+
? result.data
|
|
72
|
+
: JSON.stringify(result.data, null, 2),
|
|
73
|
+
);
|
|
74
|
+
} else if (!result.success && result.error) {
|
|
75
|
+
// Tool returned an error in JSON format
|
|
76
|
+
reject(new Error(result.error.message || "Tool execution failed"));
|
|
77
|
+
} else {
|
|
78
|
+
resolve(stdout);
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
resolve(stdout);
|
|
82
|
+
}
|
|
83
|
+
} else if (code === 2) {
|
|
84
|
+
reject(new Error(`Unknown tool: ${name}`));
|
|
85
|
+
} else if (code === 3) {
|
|
86
|
+
reject(new Error(`Invalid JSON args: ${stderr}`));
|
|
87
|
+
} else {
|
|
88
|
+
// Tool returned error
|
|
89
|
+
try {
|
|
90
|
+
const result = JSON.parse(stdout);
|
|
91
|
+
if (!result.success && result.error) {
|
|
92
|
+
reject(
|
|
93
|
+
new Error(
|
|
94
|
+
result.error.message || `Tool failed with code ${code}`,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
reject(
|
|
99
|
+
new Error(stderr || stdout || `Tool failed with code ${code}`),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
reject(
|
|
104
|
+
new Error(stderr || stdout || `Tool failed with code ${code}`),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
proc.on("error", (err) => {
|
|
111
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
112
|
+
reject(
|
|
113
|
+
new Error(
|
|
114
|
+
`swarm CLI not found. Install with: npm install -g opencode-swarm-plugin`,
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
} else {
|
|
118
|
+
reject(err);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// Beads Tools
|
|
126
|
+
// =============================================================================
|
|
127
|
+
|
|
128
|
+
const beads_create = tool({
|
|
129
|
+
description: "Create a new bead with type-safe validation",
|
|
130
|
+
args: {
|
|
131
|
+
title: tool.schema.string().describe("Bead title"),
|
|
132
|
+
type: tool.schema
|
|
133
|
+
.enum(["bug", "feature", "task", "epic", "chore"])
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("Issue type (default: task)"),
|
|
136
|
+
priority: tool.schema
|
|
137
|
+
.number()
|
|
138
|
+
.min(0)
|
|
139
|
+
.max(3)
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Priority 0-3 (default: 2)"),
|
|
142
|
+
description: tool.schema.string().optional().describe("Bead description"),
|
|
143
|
+
parent_id: tool.schema
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Parent bead ID for epic children"),
|
|
147
|
+
},
|
|
148
|
+
execute: (args, ctx) => execTool("beads_create", args, ctx),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const beads_create_epic = tool({
|
|
152
|
+
description: "Create epic with subtasks in one atomic operation",
|
|
153
|
+
args: {
|
|
154
|
+
epic_title: tool.schema.string().describe("Epic title"),
|
|
155
|
+
epic_description: tool.schema
|
|
156
|
+
.string()
|
|
157
|
+
.optional()
|
|
158
|
+
.describe("Epic description"),
|
|
159
|
+
subtasks: tool.schema
|
|
160
|
+
.array(
|
|
161
|
+
tool.schema.object({
|
|
162
|
+
title: tool.schema.string(),
|
|
163
|
+
priority: tool.schema.number().min(0).max(3).optional(),
|
|
164
|
+
files: tool.schema.array(tool.schema.string()).optional(),
|
|
165
|
+
}),
|
|
166
|
+
)
|
|
167
|
+
.describe("Subtasks to create under the epic"),
|
|
168
|
+
},
|
|
169
|
+
execute: (args, ctx) => execTool("beads_create_epic", args, ctx),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const beads_query = tool({
|
|
173
|
+
description: "Query beads with filters (replaces bd list, bd ready, bd wip)",
|
|
174
|
+
args: {
|
|
175
|
+
status: tool.schema
|
|
176
|
+
.enum(["open", "in_progress", "blocked", "closed"])
|
|
177
|
+
.optional()
|
|
178
|
+
.describe("Filter by status"),
|
|
179
|
+
type: tool.schema
|
|
180
|
+
.enum(["bug", "feature", "task", "epic", "chore"])
|
|
181
|
+
.optional()
|
|
182
|
+
.describe("Filter by type"),
|
|
183
|
+
ready: tool.schema
|
|
184
|
+
.boolean()
|
|
185
|
+
.optional()
|
|
186
|
+
.describe("Only show unblocked beads"),
|
|
187
|
+
limit: tool.schema
|
|
188
|
+
.number()
|
|
189
|
+
.optional()
|
|
190
|
+
.describe("Max results (default: 20)"),
|
|
191
|
+
},
|
|
192
|
+
execute: (args, ctx) => execTool("beads_query", args, ctx),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const beads_update = tool({
|
|
196
|
+
description: "Update bead status/description",
|
|
197
|
+
args: {
|
|
198
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
199
|
+
status: tool.schema
|
|
200
|
+
.enum(["open", "in_progress", "blocked", "closed"])
|
|
201
|
+
.optional()
|
|
202
|
+
.describe("New status"),
|
|
203
|
+
description: tool.schema.string().optional().describe("New description"),
|
|
204
|
+
priority: tool.schema
|
|
205
|
+
.number()
|
|
206
|
+
.min(0)
|
|
207
|
+
.max(3)
|
|
208
|
+
.optional()
|
|
209
|
+
.describe("New priority"),
|
|
210
|
+
},
|
|
211
|
+
execute: (args, ctx) => execTool("beads_update", args, ctx),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const beads_close = tool({
|
|
215
|
+
description: "Close a bead with reason",
|
|
216
|
+
args: {
|
|
217
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
218
|
+
reason: tool.schema.string().describe("Completion reason"),
|
|
219
|
+
},
|
|
220
|
+
execute: (args, ctx) => execTool("beads_close", args, ctx),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const beads_start = tool({
|
|
224
|
+
description: "Mark a bead as in-progress",
|
|
225
|
+
args: {
|
|
226
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
227
|
+
},
|
|
228
|
+
execute: (args, ctx) => execTool("beads_start", args, ctx),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const beads_ready = tool({
|
|
232
|
+
description: "Get the next ready bead (unblocked, highest priority)",
|
|
233
|
+
args: {},
|
|
234
|
+
execute: (args, ctx) => execTool("beads_ready", args, ctx),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const beads_sync = tool({
|
|
238
|
+
description: "Sync beads to git and push (MANDATORY at session end)",
|
|
239
|
+
args: {
|
|
240
|
+
auto_pull: tool.schema.boolean().optional().describe("Pull before sync"),
|
|
241
|
+
},
|
|
242
|
+
execute: (args, ctx) => execTool("beads_sync", args, ctx),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const beads_link_thread = tool({
|
|
246
|
+
description: "Add metadata linking bead to Agent Mail thread",
|
|
247
|
+
args: {
|
|
248
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
249
|
+
thread_id: tool.schema.string().describe("Agent Mail thread ID"),
|
|
250
|
+
},
|
|
251
|
+
execute: (args, ctx) => execTool("beads_link_thread", args, ctx),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// =============================================================================
|
|
255
|
+
// Agent Mail Tools
|
|
256
|
+
// =============================================================================
|
|
257
|
+
|
|
258
|
+
const agentmail_init = tool({
|
|
259
|
+
description: "Initialize Agent Mail session",
|
|
260
|
+
args: {
|
|
261
|
+
project_path: tool.schema.string().describe("Absolute path to the project"),
|
|
262
|
+
agent_name: tool.schema.string().optional().describe("Custom agent name"),
|
|
263
|
+
task_description: tool.schema
|
|
264
|
+
.string()
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Task description"),
|
|
267
|
+
},
|
|
268
|
+
execute: (args, ctx) => execTool("agentmail_init", args, ctx),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const agentmail_send = tool({
|
|
272
|
+
description: "Send message to other agents",
|
|
273
|
+
args: {
|
|
274
|
+
to: tool.schema
|
|
275
|
+
.array(tool.schema.string())
|
|
276
|
+
.describe("Recipient agent names"),
|
|
277
|
+
subject: tool.schema.string().describe("Message subject"),
|
|
278
|
+
body: tool.schema.string().describe("Message body"),
|
|
279
|
+
thread_id: tool.schema
|
|
280
|
+
.string()
|
|
281
|
+
.optional()
|
|
282
|
+
.describe("Thread ID for grouping"),
|
|
283
|
+
importance: tool.schema
|
|
284
|
+
.enum(["low", "normal", "high", "urgent"])
|
|
285
|
+
.optional()
|
|
286
|
+
.describe("Message importance"),
|
|
287
|
+
ack_required: tool.schema
|
|
288
|
+
.boolean()
|
|
289
|
+
.optional()
|
|
290
|
+
.describe("Require acknowledgment"),
|
|
291
|
+
},
|
|
292
|
+
execute: (args, ctx) => execTool("agentmail_send", args, ctx),
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const agentmail_inbox = tool({
|
|
296
|
+
description: "Fetch inbox (CONTEXT-SAFE: bodies excluded, limit 5)",
|
|
297
|
+
args: {
|
|
298
|
+
limit: tool.schema
|
|
299
|
+
.number()
|
|
300
|
+
.max(5)
|
|
301
|
+
.optional()
|
|
302
|
+
.describe("Max messages (max 5)"),
|
|
303
|
+
urgent_only: tool.schema
|
|
304
|
+
.boolean()
|
|
305
|
+
.optional()
|
|
306
|
+
.describe("Only urgent messages"),
|
|
307
|
+
since_ts: tool.schema
|
|
308
|
+
.string()
|
|
309
|
+
.optional()
|
|
310
|
+
.describe("Messages since timestamp"),
|
|
311
|
+
},
|
|
312
|
+
execute: (args, ctx) => execTool("agentmail_inbox", args, ctx),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const agentmail_read_message = tool({
|
|
316
|
+
description: "Fetch ONE message body by ID",
|
|
317
|
+
args: {
|
|
318
|
+
message_id: tool.schema.number().describe("Message ID"),
|
|
319
|
+
},
|
|
320
|
+
execute: (args, ctx) => execTool("agentmail_read_message", args, ctx),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const agentmail_summarize_thread = tool({
|
|
324
|
+
description: "Summarize thread (PREFERRED over fetching all messages)",
|
|
325
|
+
args: {
|
|
326
|
+
thread_id: tool.schema.string().describe("Thread ID"),
|
|
327
|
+
include_examples: tool.schema
|
|
328
|
+
.boolean()
|
|
329
|
+
.optional()
|
|
330
|
+
.describe("Include example messages"),
|
|
331
|
+
},
|
|
332
|
+
execute: (args, ctx) => execTool("agentmail_summarize_thread", args, ctx),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const agentmail_reserve = tool({
|
|
336
|
+
description: "Reserve file paths for exclusive editing",
|
|
337
|
+
args: {
|
|
338
|
+
paths: tool.schema
|
|
339
|
+
.array(tool.schema.string())
|
|
340
|
+
.describe("File paths/patterns"),
|
|
341
|
+
ttl_seconds: tool.schema.number().optional().describe("Reservation TTL"),
|
|
342
|
+
exclusive: tool.schema.boolean().optional().describe("Exclusive lock"),
|
|
343
|
+
reason: tool.schema.string().optional().describe("Reservation reason"),
|
|
344
|
+
},
|
|
345
|
+
execute: (args, ctx) => execTool("agentmail_reserve", args, ctx),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const agentmail_release = tool({
|
|
349
|
+
description: "Release file reservations",
|
|
350
|
+
args: {
|
|
351
|
+
paths: tool.schema
|
|
352
|
+
.array(tool.schema.string())
|
|
353
|
+
.optional()
|
|
354
|
+
.describe("Paths to release"),
|
|
355
|
+
reservation_ids: tool.schema
|
|
356
|
+
.array(tool.schema.number())
|
|
357
|
+
.optional()
|
|
358
|
+
.describe("Reservation IDs"),
|
|
359
|
+
},
|
|
360
|
+
execute: (args, ctx) => execTool("agentmail_release", args, ctx),
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const agentmail_ack = tool({
|
|
364
|
+
description: "Acknowledge a message",
|
|
365
|
+
args: {
|
|
366
|
+
message_id: tool.schema.number().describe("Message ID"),
|
|
367
|
+
},
|
|
368
|
+
execute: (args, ctx) => execTool("agentmail_ack", args, ctx),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const agentmail_search = tool({
|
|
372
|
+
description: "Search messages by keyword",
|
|
373
|
+
args: {
|
|
374
|
+
query: tool.schema.string().describe("Search query"),
|
|
375
|
+
limit: tool.schema.number().optional().describe("Max results"),
|
|
376
|
+
},
|
|
377
|
+
execute: (args, ctx) => execTool("agentmail_search", args, ctx),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const agentmail_health = tool({
|
|
381
|
+
description: "Check if Agent Mail server is running",
|
|
382
|
+
args: {},
|
|
383
|
+
execute: (args, ctx) => execTool("agentmail_health", args, ctx),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// =============================================================================
|
|
387
|
+
// Structured Tools
|
|
388
|
+
// =============================================================================
|
|
389
|
+
|
|
390
|
+
const structured_extract_json = tool({
|
|
391
|
+
description: "Extract JSON from markdown/text response",
|
|
392
|
+
args: {
|
|
393
|
+
text: tool.schema.string().describe("Text containing JSON"),
|
|
394
|
+
},
|
|
395
|
+
execute: (args, ctx) => execTool("structured_extract_json", args, ctx),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const structured_validate = tool({
|
|
399
|
+
description: "Validate agent response against a schema",
|
|
400
|
+
args: {
|
|
401
|
+
response: tool.schema.string().describe("Agent response to validate"),
|
|
402
|
+
schema_name: tool.schema
|
|
403
|
+
.enum(["evaluation", "task_decomposition", "bead_tree"])
|
|
404
|
+
.describe("Schema to validate against"),
|
|
405
|
+
max_retries: tool.schema
|
|
406
|
+
.number()
|
|
407
|
+
.min(1)
|
|
408
|
+
.max(5)
|
|
409
|
+
.optional()
|
|
410
|
+
.describe("Max retries"),
|
|
411
|
+
},
|
|
412
|
+
execute: (args, ctx) => execTool("structured_validate", args, ctx),
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const structured_parse_evaluation = tool({
|
|
416
|
+
description: "Parse and validate evaluation response",
|
|
417
|
+
args: {
|
|
418
|
+
response: tool.schema.string().describe("Agent response"),
|
|
419
|
+
},
|
|
420
|
+
execute: (args, ctx) => execTool("structured_parse_evaluation", args, ctx),
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const structured_parse_decomposition = tool({
|
|
424
|
+
description: "Parse and validate task decomposition response",
|
|
425
|
+
args: {
|
|
426
|
+
response: tool.schema.string().describe("Agent response"),
|
|
427
|
+
},
|
|
428
|
+
execute: (args, ctx) => execTool("structured_parse_decomposition", args, ctx),
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const structured_parse_bead_tree = tool({
|
|
432
|
+
description: "Parse and validate bead tree response",
|
|
433
|
+
args: {
|
|
434
|
+
response: tool.schema.string().describe("Agent response"),
|
|
435
|
+
},
|
|
436
|
+
execute: (args, ctx) => execTool("structured_parse_bead_tree", args, ctx),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// =============================================================================
|
|
440
|
+
// Swarm Tools
|
|
441
|
+
// =============================================================================
|
|
442
|
+
|
|
443
|
+
const swarm_init = tool({
|
|
444
|
+
description: "Initialize swarm session and check tool availability",
|
|
445
|
+
args: {
|
|
446
|
+
project_path: tool.schema.string().optional().describe("Project path"),
|
|
447
|
+
},
|
|
448
|
+
execute: (args, ctx) => execTool("swarm_init", args, ctx),
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const swarm_select_strategy = tool({
|
|
452
|
+
description: "Analyze task and recommend decomposition strategy",
|
|
453
|
+
args: {
|
|
454
|
+
task: tool.schema.string().min(1).describe("Task to analyze"),
|
|
455
|
+
codebase_context: tool.schema
|
|
456
|
+
.string()
|
|
457
|
+
.optional()
|
|
458
|
+
.describe("Codebase context"),
|
|
459
|
+
},
|
|
460
|
+
execute: (args, ctx) => execTool("swarm_select_strategy", args, ctx),
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const swarm_plan_prompt = tool({
|
|
464
|
+
description: "Generate strategy-specific decomposition prompt",
|
|
465
|
+
args: {
|
|
466
|
+
task: tool.schema.string().min(1).describe("Task to decompose"),
|
|
467
|
+
strategy: tool.schema
|
|
468
|
+
.enum(["file-based", "feature-based", "risk-based", "auto"])
|
|
469
|
+
.optional()
|
|
470
|
+
.describe("Decomposition strategy"),
|
|
471
|
+
max_subtasks: tool.schema
|
|
472
|
+
.number()
|
|
473
|
+
.int()
|
|
474
|
+
.min(2)
|
|
475
|
+
.max(10)
|
|
476
|
+
.optional()
|
|
477
|
+
.describe("Max subtasks"),
|
|
478
|
+
context: tool.schema.string().optional().describe("Additional context"),
|
|
479
|
+
query_cass: tool.schema
|
|
480
|
+
.boolean()
|
|
481
|
+
.optional()
|
|
482
|
+
.describe("Query CASS for similar tasks"),
|
|
483
|
+
cass_limit: tool.schema
|
|
484
|
+
.number()
|
|
485
|
+
.int()
|
|
486
|
+
.min(1)
|
|
487
|
+
.max(10)
|
|
488
|
+
.optional()
|
|
489
|
+
.describe("CASS limit"),
|
|
490
|
+
},
|
|
491
|
+
execute: (args, ctx) => execTool("swarm_plan_prompt", args, ctx),
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const swarm_decompose = tool({
|
|
495
|
+
description: "Generate decomposition prompt for breaking task into subtasks",
|
|
496
|
+
args: {
|
|
497
|
+
task: tool.schema.string().min(1).describe("Task to decompose"),
|
|
498
|
+
max_subtasks: tool.schema
|
|
499
|
+
.number()
|
|
500
|
+
.int()
|
|
501
|
+
.min(2)
|
|
502
|
+
.max(10)
|
|
503
|
+
.optional()
|
|
504
|
+
.describe("Max subtasks"),
|
|
505
|
+
context: tool.schema.string().optional().describe("Additional context"),
|
|
506
|
+
query_cass: tool.schema.boolean().optional().describe("Query CASS"),
|
|
507
|
+
cass_limit: tool.schema
|
|
508
|
+
.number()
|
|
509
|
+
.int()
|
|
510
|
+
.min(1)
|
|
511
|
+
.max(10)
|
|
512
|
+
.optional()
|
|
513
|
+
.describe("CASS limit"),
|
|
514
|
+
},
|
|
515
|
+
execute: (args, ctx) => execTool("swarm_decompose", args, ctx),
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
const swarm_validate_decomposition = tool({
|
|
519
|
+
description: "Validate a decomposition response against BeadTreeSchema",
|
|
520
|
+
args: {
|
|
521
|
+
response: tool.schema.string().describe("Decomposition response"),
|
|
522
|
+
},
|
|
523
|
+
execute: (args, ctx) => execTool("swarm_validate_decomposition", args, ctx),
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const swarm_status = tool({
|
|
527
|
+
description: "Get status of a swarm by epic ID",
|
|
528
|
+
args: {
|
|
529
|
+
epic_id: tool.schema.string().describe("Epic bead ID"),
|
|
530
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
531
|
+
},
|
|
532
|
+
execute: (args, ctx) => execTool("swarm_status", args, ctx),
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
const swarm_progress = tool({
|
|
536
|
+
description: "Report progress on a subtask to coordinator",
|
|
537
|
+
args: {
|
|
538
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
539
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
540
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
541
|
+
status: tool.schema
|
|
542
|
+
.enum(["in_progress", "blocked", "completed", "failed"])
|
|
543
|
+
.describe("Status"),
|
|
544
|
+
message: tool.schema.string().optional().describe("Progress message"),
|
|
545
|
+
progress_percent: tool.schema
|
|
546
|
+
.number()
|
|
547
|
+
.min(0)
|
|
548
|
+
.max(100)
|
|
549
|
+
.optional()
|
|
550
|
+
.describe("Progress %"),
|
|
551
|
+
files_touched: tool.schema
|
|
552
|
+
.array(tool.schema.string())
|
|
553
|
+
.optional()
|
|
554
|
+
.describe("Files modified"),
|
|
555
|
+
},
|
|
556
|
+
execute: (args, ctx) => execTool("swarm_progress", args, ctx),
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
const swarm_complete = tool({
|
|
560
|
+
description:
|
|
561
|
+
"Mark subtask complete, release reservations, notify coordinator",
|
|
562
|
+
args: {
|
|
563
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
564
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
565
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
566
|
+
summary: tool.schema.string().describe("Completion summary"),
|
|
567
|
+
evaluation: tool.schema.string().optional().describe("Self-evaluation"),
|
|
568
|
+
files_touched: tool.schema
|
|
569
|
+
.array(tool.schema.string())
|
|
570
|
+
.optional()
|
|
571
|
+
.describe("Files modified"),
|
|
572
|
+
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS scan"),
|
|
573
|
+
},
|
|
574
|
+
execute: (args, ctx) => execTool("swarm_complete", args, ctx),
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const swarm_record_outcome = tool({
|
|
578
|
+
description: "Record subtask outcome for implicit feedback scoring",
|
|
579
|
+
args: {
|
|
580
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
581
|
+
duration_ms: tool.schema.number().int().min(0).describe("Duration in ms"),
|
|
582
|
+
error_count: tool.schema
|
|
583
|
+
.number()
|
|
584
|
+
.int()
|
|
585
|
+
.min(0)
|
|
586
|
+
.optional()
|
|
587
|
+
.describe("Error count"),
|
|
588
|
+
retry_count: tool.schema
|
|
589
|
+
.number()
|
|
590
|
+
.int()
|
|
591
|
+
.min(0)
|
|
592
|
+
.optional()
|
|
593
|
+
.describe("Retry count"),
|
|
594
|
+
success: tool.schema.boolean().describe("Whether task succeeded"),
|
|
595
|
+
files_touched: tool.schema
|
|
596
|
+
.array(tool.schema.string())
|
|
597
|
+
.optional()
|
|
598
|
+
.describe("Files modified"),
|
|
599
|
+
criteria: tool.schema
|
|
600
|
+
.array(tool.schema.string())
|
|
601
|
+
.optional()
|
|
602
|
+
.describe("Evaluation criteria"),
|
|
603
|
+
strategy: tool.schema
|
|
604
|
+
.enum(["file-based", "feature-based", "risk-based"])
|
|
605
|
+
.optional()
|
|
606
|
+
.describe("Strategy used"),
|
|
607
|
+
},
|
|
608
|
+
execute: (args, ctx) => execTool("swarm_record_outcome", args, ctx),
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
const swarm_subtask_prompt = tool({
|
|
612
|
+
description: "Generate the prompt for a spawned subtask agent",
|
|
613
|
+
args: {
|
|
614
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
615
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
616
|
+
epic_id: tool.schema.string().describe("Epic ID"),
|
|
617
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
618
|
+
subtask_description: tool.schema
|
|
619
|
+
.string()
|
|
620
|
+
.optional()
|
|
621
|
+
.describe("Description"),
|
|
622
|
+
files: tool.schema.array(tool.schema.string()).describe("Files to work on"),
|
|
623
|
+
shared_context: tool.schema.string().optional().describe("Shared context"),
|
|
624
|
+
},
|
|
625
|
+
execute: (args, ctx) => execTool("swarm_subtask_prompt", args, ctx),
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const swarm_spawn_subtask = tool({
|
|
629
|
+
description: "Prepare a subtask for spawning with Task tool",
|
|
630
|
+
args: {
|
|
631
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
632
|
+
epic_id: tool.schema.string().describe("Epic ID"),
|
|
633
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
634
|
+
subtask_description: tool.schema
|
|
635
|
+
.string()
|
|
636
|
+
.optional()
|
|
637
|
+
.describe("Description"),
|
|
638
|
+
files: tool.schema.array(tool.schema.string()).describe("Files to work on"),
|
|
639
|
+
shared_context: tool.schema.string().optional().describe("Shared context"),
|
|
640
|
+
},
|
|
641
|
+
execute: (args, ctx) => execTool("swarm_spawn_subtask", args, ctx),
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
const swarm_complete_subtask = tool({
|
|
645
|
+
description: "Handle subtask completion after Task agent returns",
|
|
646
|
+
args: {
|
|
647
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
648
|
+
task_result: tool.schema.string().describe("Task result JSON"),
|
|
649
|
+
files_touched: tool.schema
|
|
650
|
+
.array(tool.schema.string())
|
|
651
|
+
.optional()
|
|
652
|
+
.describe("Files modified"),
|
|
653
|
+
},
|
|
654
|
+
execute: (args, ctx) => execTool("swarm_complete_subtask", args, ctx),
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
const swarm_evaluation_prompt = tool({
|
|
658
|
+
description: "Generate self-evaluation prompt for a completed subtask",
|
|
659
|
+
args: {
|
|
660
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
661
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
662
|
+
files_touched: tool.schema
|
|
663
|
+
.array(tool.schema.string())
|
|
664
|
+
.describe("Files modified"),
|
|
665
|
+
},
|
|
666
|
+
execute: (args, ctx) => execTool("swarm_evaluation_prompt", args, ctx),
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// =============================================================================
|
|
670
|
+
// Plugin Export
|
|
671
|
+
// =============================================================================
|
|
672
|
+
|
|
673
|
+
export const SwarmPlugin: Plugin = async (
|
|
674
|
+
_input: PluginInput,
|
|
675
|
+
): Promise<Hooks> => {
|
|
676
|
+
return {
|
|
677
|
+
tool: {
|
|
678
|
+
// Beads
|
|
679
|
+
beads_create,
|
|
680
|
+
beads_create_epic,
|
|
681
|
+
beads_query,
|
|
682
|
+
beads_update,
|
|
683
|
+
beads_close,
|
|
684
|
+
beads_start,
|
|
685
|
+
beads_ready,
|
|
686
|
+
beads_sync,
|
|
687
|
+
beads_link_thread,
|
|
688
|
+
// Agent Mail
|
|
689
|
+
agentmail_init,
|
|
690
|
+
agentmail_send,
|
|
691
|
+
agentmail_inbox,
|
|
692
|
+
agentmail_read_message,
|
|
693
|
+
agentmail_summarize_thread,
|
|
694
|
+
agentmail_reserve,
|
|
695
|
+
agentmail_release,
|
|
696
|
+
agentmail_ack,
|
|
697
|
+
agentmail_search,
|
|
698
|
+
agentmail_health,
|
|
699
|
+
// Structured
|
|
700
|
+
structured_extract_json,
|
|
701
|
+
structured_validate,
|
|
702
|
+
structured_parse_evaluation,
|
|
703
|
+
structured_parse_decomposition,
|
|
704
|
+
structured_parse_bead_tree,
|
|
705
|
+
// Swarm
|
|
706
|
+
swarm_init,
|
|
707
|
+
swarm_select_strategy,
|
|
708
|
+
swarm_plan_prompt,
|
|
709
|
+
swarm_decompose,
|
|
710
|
+
swarm_validate_decomposition,
|
|
711
|
+
swarm_status,
|
|
712
|
+
swarm_progress,
|
|
713
|
+
swarm_complete,
|
|
714
|
+
swarm_record_outcome,
|
|
715
|
+
swarm_subtask_prompt,
|
|
716
|
+
swarm_spawn_subtask,
|
|
717
|
+
swarm_complete_subtask,
|
|
718
|
+
swarm_evaluation_prompt,
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
export default SwarmPlugin;
|
package/package.json
CHANGED
package/src/agent-mail.ts
CHANGED
|
@@ -62,9 +62,83 @@ export interface AgentMailState {
|
|
|
62
62
|
// Module-level state (keyed by sessionID)
|
|
63
63
|
// ============================================================================
|
|
64
64
|
|
|
65
|
+
import {
|
|
66
|
+
existsSync,
|
|
67
|
+
mkdirSync,
|
|
68
|
+
readFileSync,
|
|
69
|
+
writeFileSync,
|
|
70
|
+
unlinkSync,
|
|
71
|
+
} from "fs";
|
|
72
|
+
import { join } from "path";
|
|
73
|
+
import { tmpdir } from "os";
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Directory for persisting session state across CLI invocations
|
|
77
|
+
* This allows `swarm tool` commands to share state
|
|
78
|
+
*/
|
|
79
|
+
const SESSION_STATE_DIR =
|
|
80
|
+
process.env.SWARM_STATE_DIR || join(tmpdir(), "swarm-sessions");
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the file path for a session's state
|
|
84
|
+
*/
|
|
85
|
+
function getSessionStatePath(sessionID: string): string {
|
|
86
|
+
// Sanitize sessionID to be filesystem-safe
|
|
87
|
+
const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
88
|
+
return join(SESSION_STATE_DIR, `${safeID}.json`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Load session state from disk
|
|
93
|
+
*/
|
|
94
|
+
function loadSessionState(sessionID: string): AgentMailState | null {
|
|
95
|
+
const path = getSessionStatePath(sessionID);
|
|
96
|
+
try {
|
|
97
|
+
if (existsSync(path)) {
|
|
98
|
+
const data = readFileSync(path, "utf-8");
|
|
99
|
+
return JSON.parse(data) as AgentMailState;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// File might be corrupted or inaccessible - ignore and return null
|
|
103
|
+
console.warn(`[agent-mail] Could not load session state: ${error}`);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Save session state to disk
|
|
110
|
+
*/
|
|
111
|
+
function saveSessionState(sessionID: string, state: AgentMailState): void {
|
|
112
|
+
try {
|
|
113
|
+
// Ensure directory exists
|
|
114
|
+
if (!existsSync(SESSION_STATE_DIR)) {
|
|
115
|
+
mkdirSync(SESSION_STATE_DIR, { recursive: true });
|
|
116
|
+
}
|
|
117
|
+
const path = getSessionStatePath(sessionID);
|
|
118
|
+
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Non-fatal - state just won't persist
|
|
121
|
+
console.warn(`[agent-mail] Could not save session state: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Delete session state from disk
|
|
127
|
+
*/
|
|
128
|
+
function deleteSessionState(sessionID: string): void {
|
|
129
|
+
const path = getSessionStatePath(sessionID);
|
|
130
|
+
try {
|
|
131
|
+
if (existsSync(path)) {
|
|
132
|
+
unlinkSync(path);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
// Ignore errors on cleanup
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
65
139
|
/**
|
|
66
140
|
* State storage keyed by sessionID.
|
|
67
|
-
*
|
|
141
|
+
* In-memory cache that also persists to disk for CLI usage.
|
|
68
142
|
*/
|
|
69
143
|
const sessionStates = new Map<string, AgentMailState>();
|
|
70
144
|
|
|
@@ -691,9 +765,23 @@ export async function mcpCall<T>(
|
|
|
691
765
|
|
|
692
766
|
/**
|
|
693
767
|
* Get Agent Mail state for a session, or throw if not initialized
|
|
768
|
+
*
|
|
769
|
+
* Checks in-memory cache first, then falls back to disk storage.
|
|
770
|
+
* This allows CLI invocations to share state across calls.
|
|
694
771
|
*/
|
|
695
772
|
function requireState(sessionID: string): AgentMailState {
|
|
696
|
-
|
|
773
|
+
// Check in-memory cache first
|
|
774
|
+
let state = sessionStates.get(sessionID);
|
|
775
|
+
|
|
776
|
+
// If not in memory, try loading from disk
|
|
777
|
+
if (!state) {
|
|
778
|
+
state = loadSessionState(sessionID) ?? undefined;
|
|
779
|
+
if (state) {
|
|
780
|
+
// Cache in memory for subsequent calls in same process
|
|
781
|
+
sessionStates.set(sessionID, state);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
697
785
|
if (!state) {
|
|
698
786
|
throw new AgentMailNotInitializedError();
|
|
699
787
|
}
|
|
@@ -702,23 +790,38 @@ function requireState(sessionID: string): AgentMailState {
|
|
|
702
790
|
|
|
703
791
|
/**
|
|
704
792
|
* Store Agent Mail state for a session
|
|
793
|
+
*
|
|
794
|
+
* Saves to both in-memory cache and disk for CLI persistence.
|
|
705
795
|
*/
|
|
706
796
|
function setState(sessionID: string, state: AgentMailState): void {
|
|
707
797
|
sessionStates.set(sessionID, state);
|
|
798
|
+
saveSessionState(sessionID, state);
|
|
708
799
|
}
|
|
709
800
|
|
|
710
801
|
/**
|
|
711
802
|
* Get state if exists (for cleanup hooks)
|
|
803
|
+
*
|
|
804
|
+
* Checks in-memory cache first, then falls back to disk storage.
|
|
712
805
|
*/
|
|
713
806
|
function getState(sessionID: string): AgentMailState | undefined {
|
|
714
|
-
|
|
807
|
+
let state = sessionStates.get(sessionID);
|
|
808
|
+
if (!state) {
|
|
809
|
+
state = loadSessionState(sessionID) ?? undefined;
|
|
810
|
+
if (state) {
|
|
811
|
+
sessionStates.set(sessionID, state);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return state;
|
|
715
815
|
}
|
|
716
816
|
|
|
717
817
|
/**
|
|
718
818
|
* Clear state for a session
|
|
819
|
+
*
|
|
820
|
+
* Removes from both in-memory cache and disk.
|
|
719
821
|
*/
|
|
720
822
|
function clearState(sessionID: string): void {
|
|
721
823
|
sessionStates.delete(sessionID);
|
|
824
|
+
deleteSessionState(sessionID);
|
|
722
825
|
}
|
|
723
826
|
|
|
724
827
|
// ============================================================================
|
package/src/index.ts
CHANGED
|
@@ -280,6 +280,28 @@ export {
|
|
|
280
280
|
type StrategyDefinition,
|
|
281
281
|
} from "./swarm";
|
|
282
282
|
|
|
283
|
+
// =============================================================================
|
|
284
|
+
// Unified Tool Registry for CLI
|
|
285
|
+
// =============================================================================
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* All tools in a single registry for CLI tool execution
|
|
289
|
+
*
|
|
290
|
+
* This is used by `swarm tool <name>` command to dynamically execute tools.
|
|
291
|
+
* Each tool has an `execute` function that takes (args, ctx) and returns a string.
|
|
292
|
+
*/
|
|
293
|
+
export const allTools = {
|
|
294
|
+
...beadsTools,
|
|
295
|
+
...agentMailTools,
|
|
296
|
+
...structuredTools,
|
|
297
|
+
...swarmTools,
|
|
298
|
+
} as const;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Type for CLI tool names (all available tools)
|
|
302
|
+
*/
|
|
303
|
+
export type CLIToolName = keyof typeof allTools;
|
|
304
|
+
|
|
283
305
|
/**
|
|
284
306
|
* Re-export storage module
|
|
285
307
|
*
|