adhdev 0.9.76-rc.2 → 0.9.76-rc.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adhdev",
3
- "version": "0.9.76-rc.2",
3
+ "version": "0.9.76-rc.20",
4
4
  "description": "ADHDev — Agent Dashboard Hub for Dev. Remote-control AI coding agents from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "node": ">=18"
48
48
  },
49
49
  "dependencies": {
50
- "@adhdev/daemon-core": "0.9.76-rc.2",
50
+ "@adhdev/daemon-core": "0.9.76-rc.20",
51
51
  "@adhdev/ghostty-vt-node": "*",
52
52
  "@modelcontextprotocol/sdk": "^1.0.0",
53
53
  "@xterm/addon-serialize": "^0.14.0",
@@ -6,6 +6,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
9
13
  var __copyProps = (to, from, except, desc) => {
10
14
  if (from && typeof from === "object" || typeof from === "function") {
11
15
  for (let key of __getOwnPropNames(from))
@@ -22,6 +26,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
26
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
27
  mod
24
28
  ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/index.ts
32
+ var index_exports = {};
33
+ __export(index_exports, {
34
+ parseArgs: () => parseArgs
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
25
37
 
26
38
  // src/server.ts
27
39
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
@@ -234,6 +246,107 @@ var CloudTransport = class {
234
246
  }
235
247
  };
236
248
 
249
+ // src/transports/ipc.ts
250
+ var DEFAULT_IPC_PORT = 19222;
251
+ var DEFAULT_IPC_PATH = "/ipc";
252
+ var IpcTransport = class {
253
+ port;
254
+ path;
255
+ constructor(opts = {}) {
256
+ this.port = opts.port ?? DEFAULT_IPC_PORT;
257
+ this.path = opts.path || DEFAULT_IPC_PATH;
258
+ }
259
+ async ping() {
260
+ try {
261
+ const res = await fetch(`http://127.0.0.1:${this.port}/health`);
262
+ return res.ok;
263
+ } catch {
264
+ return false;
265
+ }
266
+ }
267
+ async getStatus() {
268
+ return this.command("get_status_metadata");
269
+ }
270
+ async command(type, args = {}) {
271
+ return this.sendIpcCommand(type, args);
272
+ }
273
+ async meshCommand(targetDaemonId, command, args = {}) {
274
+ return this.sendIpcCommand("mesh_relay_command", {
275
+ targetDaemonId,
276
+ command,
277
+ args
278
+ });
279
+ }
280
+ async sendIpcCommand(type, args) {
281
+ const WebSocketCtor = globalThis.WebSocket;
282
+ if (!WebSocketCtor) {
283
+ throw new Error("WebSocket is not available in this Node runtime; Node 20+ is required for daemon IPC mode");
284
+ }
285
+ return new Promise((resolve, reject) => {
286
+ const requestId = `mcp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
287
+ const ws = new WebSocketCtor(`ws://127.0.0.1:${this.port}${this.path}`);
288
+ let settled = false;
289
+ const finish = (fn) => {
290
+ if (settled) return;
291
+ settled = true;
292
+ clearTimeout(timeout);
293
+ try {
294
+ ws.close();
295
+ } catch {
296
+ }
297
+ fn();
298
+ };
299
+ const timeout = setTimeout(() => {
300
+ finish(() => reject(new Error(`Daemon IPC command '${type}' timed out after 15s`)));
301
+ }, 15e3);
302
+ let commandSent = false;
303
+ const send = () => {
304
+ if (commandSent) return;
305
+ commandSent = true;
306
+ ws.send(JSON.stringify({
307
+ type: "ext:command",
308
+ payload: { command: type, args, requestId }
309
+ }));
310
+ };
311
+ ws.addEventListener("open", () => {
312
+ ws.send(JSON.stringify({
313
+ type: "ext:register",
314
+ payload: {
315
+ ideType: "mcp-server",
316
+ ideVersion: "1.0.0",
317
+ extensionVersion: "1.0.0",
318
+ instanceId: `mcp-server-${process.pid}`,
319
+ machineId: "mcp-server",
320
+ workspaceFolders: []
321
+ }
322
+ }));
323
+ });
324
+ ws.addEventListener("message", (event) => {
325
+ try {
326
+ const raw = typeof event.data === "string" ? event.data : String(event.data);
327
+ const msg = JSON.parse(raw);
328
+ if (msg?.type === "daemon:welcome") {
329
+ send();
330
+ return;
331
+ }
332
+ if (msg?.type !== "ext:command_result") return;
333
+ if (msg?.payload?.requestId !== requestId) return;
334
+ const payload = msg.payload;
335
+ if (payload?.success === false) {
336
+ finish(() => reject(new Error(payload.error || `Daemon IPC command '${type}' failed`)));
337
+ return;
338
+ }
339
+ finish(() => resolve(payload?.result ?? payload));
340
+ } catch {
341
+ }
342
+ });
343
+ ws.addEventListener("error", () => {
344
+ finish(() => reject(new Error(`Cannot connect to daemon IPC at ws://127.0.0.1:${this.port}${this.path}`)));
345
+ });
346
+ });
347
+ }
348
+ };
349
+
237
350
  // src/transports/mode.ts
238
351
  function isLocalTransport(transport) {
239
352
  return typeof transport.command === "function";
@@ -1265,10 +1378,69 @@ ${lines.join("\n\n")}`;
1265
1378
  }
1266
1379
 
1267
1380
  // src/tools/mesh-tools.ts
1268
- function findNode(mesh, nodeId) {
1269
- const node = mesh.nodes.find((n) => n.id === nodeId);
1270
- if (!node) throw new Error(`Node '${nodeId}' is not a member of mesh '${mesh.name}'`);
1271
- return node;
1381
+ async function refreshMeshFromDaemon(ctx) {
1382
+ if (!(ctx.transport instanceof IpcTransport)) return;
1383
+ try {
1384
+ const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
1385
+ if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
1386
+ const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
1387
+ if (!refreshedNodes.length) return;
1388
+ ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...refreshedNodes);
1389
+ ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
1390
+ } catch {
1391
+ }
1392
+ }
1393
+ async function findNodeWithRefresh(ctx, nodeId) {
1394
+ const hit = ctx.mesh.nodes.find((n) => n.id === nodeId);
1395
+ if (hit) return hit;
1396
+ await refreshMeshFromDaemon(ctx);
1397
+ const refreshed = ctx.mesh.nodes.find((n) => n.id === nodeId);
1398
+ if (!refreshed) throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
1399
+ return refreshed;
1400
+ }
1401
+ function unwrapCommandPayload(value) {
1402
+ return value?.result?.result ?? value?.result ?? value;
1403
+ }
1404
+ function findNestedPayload(value, predicate) {
1405
+ let cursor = value;
1406
+ for (let depth = 0; depth < 8; depth += 1) {
1407
+ if (predicate(cursor)) return cursor;
1408
+ if (!cursor || typeof cursor !== "object" || !("result" in cursor)) break;
1409
+ cursor = cursor.result;
1410
+ }
1411
+ return value;
1412
+ }
1413
+ function extractCloneNodePayload(value) {
1414
+ return findNestedPayload(value, (payload) => Boolean(payload?.node?.id));
1415
+ }
1416
+ function extractGitStatus(value) {
1417
+ const payload = unwrapCommandPayload(value);
1418
+ return payload?.status ?? value?.status ?? payload;
1419
+ }
1420
+ function extractGitDiff(value) {
1421
+ const payload = unwrapCommandPayload(value);
1422
+ return payload?.diffSummary ?? payload?.diff ?? value?.diffSummary ?? value?.diff ?? payload;
1423
+ }
1424
+ function countUncommittedChanges(status) {
1425
+ if (typeof status?.uncommittedChanges === "number") return status.uncommittedChanges;
1426
+ const keys = ["staged", "modified", "untracked", "deleted", "renamed"];
1427
+ const counted = keys.reduce((sum, key) => sum + (Number.isFinite(Number(status?.[key])) ? Number(status[key]) : 0), 0);
1428
+ const conflicts = Array.isArray(status?.conflictFiles) ? status.conflictFiles.length : status?.hasConflicts ? 1 : 0;
1429
+ return counted + conflicts;
1430
+ }
1431
+ function isGitStatusDirty(status) {
1432
+ if (typeof status?.isDirty === "boolean") return status.isDirty;
1433
+ if (typeof status?.dirty === "boolean") return status.dirty;
1434
+ return countUncommittedChanges(status) > 0;
1435
+ }
1436
+ async function commandForNode(ctx, node, command, args = {}) {
1437
+ if (ctx.transport instanceof IpcTransport && node.daemonId) {
1438
+ return ctx.transport.meshCommand(node.daemonId, command, args);
1439
+ }
1440
+ if (isLocalTransport(ctx.transport)) {
1441
+ return ctx.transport.command(command, args);
1442
+ }
1443
+ throw new Error(`Command '${command}' requires daemon IPC/local transport for node '${node.id}'`);
1272
1444
  }
1273
1445
  var MESH_STATUS_TOOL = {
1274
1446
  name: "mesh_status",
@@ -1314,12 +1486,12 @@ var MESH_READ_CHAT_TOOL = {
1314
1486
  };
1315
1487
  var MESH_LAUNCH_SESSION_TOOL = {
1316
1488
  name: "mesh_launch_session",
1317
- description: "Launch a new agent session on a mesh node. Returns the session ID for subsequent send_task/read_chat calls.",
1489
+ description: "Launch a new agent session on a mesh node. Returns the session ID for subsequent send_task/read_chat calls. If the user names a provider, preserve it exactly: Hermes = hermes-cli, Claude Code/Claude = claude-cli, Codex = codex-cli, Gemini = gemini-cli. Do not default to claude-cli unless the user requested Claude Code or no provider was specified.",
1318
1490
  inputSchema: {
1319
1491
  type: "object",
1320
1492
  properties: {
1321
1493
  node_id: { type: "string", description: "Target node ID." },
1322
- type: { type: "string", description: 'Provider type (e.g. "claude-cli", "gemini-cli", "cursor").' }
1494
+ type: { type: "string", description: "Provider type to launch. Use hermes-cli for Hermes, claude-cli for Claude Code, codex-cli for Codex, gemini-cli for Gemini." }
1323
1495
  },
1324
1496
  required: ["node_id", "type"]
1325
1497
  }
@@ -1360,6 +1532,30 @@ var MESH_APPROVE_TOOL = {
1360
1532
  required: ["node_id", "session_id", "action"]
1361
1533
  }
1362
1534
  };
1535
+ var MESH_CLONE_NODE_TOOL = {
1536
+ name: "mesh_clone_node",
1537
+ description: "Create a new worktree-based node from an existing node for isolated parallel work. Creates a git worktree on a new branch so multiple tasks can run on separate branches simultaneously.",
1538
+ inputSchema: {
1539
+ type: "object",
1540
+ properties: {
1541
+ source_node_id: { type: "string", description: "Node ID to clone from (from mesh_list_nodes)." },
1542
+ branch: { type: "string", description: 'Branch name for the new worktree (e.g. "feat/auth-refactor").' },
1543
+ base_branch: { type: "string", description: "Starting point for the branch (default: current HEAD)." }
1544
+ },
1545
+ required: ["source_node_id", "branch"]
1546
+ }
1547
+ };
1548
+ var MESH_REMOVE_NODE_TOOL = {
1549
+ name: "mesh_remove_node",
1550
+ description: "Remove a node from the mesh. If the node is a worktree, also cleans up the git worktree and directory.",
1551
+ inputSchema: {
1552
+ type: "object",
1553
+ properties: {
1554
+ node_id: { type: "string", description: "Node ID to remove." }
1555
+ },
1556
+ required: ["node_id"]
1557
+ }
1558
+ };
1363
1559
  var ALL_MESH_TOOLS = [
1364
1560
  MESH_STATUS_TOOL,
1365
1561
  MESH_LIST_NODES_TOOL,
@@ -1368,9 +1564,12 @@ var ALL_MESH_TOOLS = [
1368
1564
  MESH_LAUNCH_SESSION_TOOL,
1369
1565
  MESH_GIT_STATUS_TOOL,
1370
1566
  MESH_CHECKPOINT_TOOL,
1371
- MESH_APPROVE_TOOL
1567
+ MESH_APPROVE_TOOL,
1568
+ MESH_CLONE_NODE_TOOL,
1569
+ MESH_REMOVE_NODE_TOOL
1372
1570
  ];
1373
1571
  async function meshStatus(ctx) {
1572
+ await refreshMeshFromDaemon(ctx);
1374
1573
  const { mesh, transport } = ctx;
1375
1574
  const results = [];
1376
1575
  for (const node of mesh.nodes) {
@@ -1381,18 +1580,22 @@ async function meshStatus(ctx) {
1381
1580
  try {
1382
1581
  if (!isLocalTransport(transport) && node.daemonId) {
1383
1582
  const result = await transport.gitStatus(node.daemonId, node.workspace, false);
1384
- const status = result?.status ?? result;
1385
- entry.health = status?.isGitRepo ? status?.isDirty ? "dirty" : "online" : "degraded";
1583
+ const status = extractGitStatus(result);
1584
+ const uncommittedChanges = countUncommittedChanges(status);
1585
+ const dirty = isGitStatusDirty(status);
1586
+ entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1386
1587
  entry.branch = status?.branch;
1387
- entry.isDirty = status?.isDirty;
1388
- entry.uncommittedChanges = status?.uncommittedChanges ?? 0;
1588
+ entry.isDirty = dirty;
1589
+ entry.uncommittedChanges = uncommittedChanges;
1389
1590
  } else if (isLocalTransport(transport)) {
1390
- const statusResult = await transport.command("git_status", { workspace: node.workspace });
1391
- const status = statusResult?.status ?? statusResult;
1392
- entry.health = status?.isGitRepo ? status?.isDirty ? "dirty" : "online" : "degraded";
1591
+ const statusResult = await commandForNode(ctx, node, "git_status", { workspace: node.workspace });
1592
+ const status = extractGitStatus(statusResult);
1593
+ const uncommittedChanges = countUncommittedChanges(status);
1594
+ const dirty = isGitStatusDirty(status);
1595
+ entry.health = status?.isGitRepo ? dirty ? "dirty" : "online" : "degraded";
1393
1596
  entry.branch = status?.branch;
1394
- entry.isDirty = status?.isDirty;
1395
- entry.uncommittedChanges = status?.uncommittedChanges ?? 0;
1597
+ entry.isDirty = dirty;
1598
+ entry.uncommittedChanges = uncommittedChanges;
1396
1599
  } else {
1397
1600
  entry.health = "unknown";
1398
1601
  entry.note = "No daemonId available for cloud status probe";
@@ -1413,6 +1616,7 @@ async function meshStatus(ctx) {
1413
1616
  }, null, 2);
1414
1617
  }
1415
1618
  async function meshListNodes(ctx) {
1619
+ await refreshMeshFromDaemon(ctx);
1416
1620
  const { mesh } = ctx;
1417
1621
  return JSON.stringify({
1418
1622
  meshId: mesh.id,
@@ -1428,14 +1632,15 @@ async function meshListNodes(ctx) {
1428
1632
  }, null, 2);
1429
1633
  }
1430
1634
  async function meshSendTask(ctx, args) {
1431
- const node = findNode(ctx.mesh, args.node_id);
1635
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1432
1636
  if (node.policy?.readOnly) {
1433
1637
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only` });
1434
1638
  }
1435
1639
  if (isLocalTransport(ctx.transport)) {
1436
- await ctx.transport.command("send_chat", {
1640
+ await commandForNode(ctx, node, "send_chat", {
1437
1641
  message: args.message,
1438
- sessionId: args.session_id
1642
+ sessionId: args.session_id,
1643
+ targetSessionId: args.session_id
1439
1644
  });
1440
1645
  return JSON.stringify({ success: true, nodeId: args.node_id, sessionId: args.session_id });
1441
1646
  } else {
@@ -1443,10 +1648,11 @@ async function meshSendTask(ctx, args) {
1443
1648
  }
1444
1649
  }
1445
1650
  async function meshReadChat(ctx, args) {
1446
- findNode(ctx.mesh, args.node_id);
1651
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1447
1652
  if (isLocalTransport(ctx.transport)) {
1448
- const result = await ctx.transport.command("read_chat", {
1653
+ const result = await commandForNode(ctx, node, "read_chat", {
1449
1654
  sessionId: args.session_id,
1655
+ targetSessionId: args.session_id,
1450
1656
  tailLimit: args.tail ?? 10
1451
1657
  });
1452
1658
  return JSON.stringify(result, null, 2);
@@ -1455,9 +1661,9 @@ async function meshReadChat(ctx, args) {
1455
1661
  }
1456
1662
  }
1457
1663
  async function meshLaunchSession(ctx, args) {
1458
- const node = findNode(ctx.mesh, args.node_id);
1664
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1459
1665
  if (isLocalTransport(ctx.transport)) {
1460
- const result = await ctx.transport.command("launch_cli", {
1666
+ const result = await commandForNode(ctx, node, "launch_cli", {
1461
1667
  cliType: args.type,
1462
1668
  dir: node.workspace,
1463
1669
  settings: {
@@ -1471,39 +1677,39 @@ async function meshLaunchSession(ctx, args) {
1471
1677
  }
1472
1678
  }
1473
1679
  async function meshGitStatus(ctx, args) {
1474
- const node = findNode(ctx.mesh, args.node_id);
1680
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1475
1681
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
1476
1682
  const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
1477
1683
  return JSON.stringify({
1478
1684
  nodeId: args.node_id,
1479
1685
  workspace: node.workspace,
1480
- status: result?.status ?? result,
1481
- diff: result?.diff ?? null
1686
+ status: extractGitStatus(result),
1687
+ diff: extractGitDiff(result)
1482
1688
  }, null, 2);
1483
1689
  } else if (isLocalTransport(ctx.transport)) {
1484
- const statusResult = await ctx.transport.command("git_status", {
1690
+ const statusResult = await commandForNode(ctx, node, "git_status", {
1485
1691
  workspace: node.workspace
1486
1692
  });
1487
- const diffResult = await ctx.transport.command("git_diff_summary", {
1693
+ const diffResult = await commandForNode(ctx, node, "git_diff_summary", {
1488
1694
  workspace: node.workspace
1489
1695
  });
1490
1696
  return JSON.stringify({
1491
1697
  nodeId: args.node_id,
1492
1698
  workspace: node.workspace,
1493
- status: statusResult?.status ?? statusResult,
1494
- diff: diffResult?.diffSummary ?? diffResult
1699
+ status: extractGitStatus(statusResult),
1700
+ diff: extractGitDiff(diffResult)
1495
1701
  }, null, 2);
1496
1702
  } else {
1497
1703
  return JSON.stringify({ error: "No daemonId available for cloud git_status probe" });
1498
1704
  }
1499
1705
  }
1500
1706
  async function meshCheckpoint(ctx, args) {
1501
- const node = findNode(ctx.mesh, args.node_id);
1707
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1502
1708
  if (node.policy?.readOnly) {
1503
1709
  return JSON.stringify({ error: `Node '${args.node_id}' is read-only \u2014 cannot checkpoint` });
1504
1710
  }
1505
1711
  if (isLocalTransport(ctx.transport)) {
1506
- const result = await ctx.transport.command("git_checkpoint", {
1712
+ const result = await commandForNode(ctx, node, "git_checkpoint", {
1507
1713
  workspace: node.workspace,
1508
1714
  message: args.message
1509
1715
  });
@@ -1513,10 +1719,11 @@ async function meshCheckpoint(ctx, args) {
1513
1719
  }
1514
1720
  }
1515
1721
  async function meshApprove(ctx, args) {
1516
- findNode(ctx.mesh, args.node_id);
1722
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1517
1723
  if (isLocalTransport(ctx.transport)) {
1518
- const result = await ctx.transport.command("resolve_action", {
1724
+ const result = await commandForNode(ctx, node, "resolve_action", {
1519
1725
  sessionId: args.session_id,
1726
+ targetSessionId: args.session_id,
1520
1727
  action: args.action === "reject" ? "reject" : "approve"
1521
1728
  });
1522
1729
  return JSON.stringify(result, null, 2);
@@ -1524,13 +1731,63 @@ async function meshApprove(ctx, args) {
1524
1731
  return JSON.stringify({ error: "Cloud mesh approve not yet implemented" });
1525
1732
  }
1526
1733
  }
1734
+ async function meshCloneNode(ctx, args) {
1735
+ const sourceNode = await findNodeWithRefresh(ctx, args.source_node_id);
1736
+ if (isLocalTransport(ctx.transport)) {
1737
+ const result = await commandForNode(ctx, sourceNode, "clone_mesh_node", {
1738
+ meshId: ctx.mesh.id,
1739
+ sourceNodeId: args.source_node_id,
1740
+ branch: args.branch,
1741
+ baseBranch: args.base_branch,
1742
+ inlineMesh: ctx.mesh
1743
+ });
1744
+ const clonePayload = extractCloneNodePayload(result);
1745
+ if (clonePayload?.success && clonePayload.node?.id) {
1746
+ const existingIndex = ctx.mesh.nodes.findIndex((n) => n.id === clonePayload.node.id);
1747
+ if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = clonePayload.node;
1748
+ else ctx.mesh.nodes.push(clonePayload.node);
1749
+ ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1750
+ }
1751
+ return JSON.stringify(result, null, 2);
1752
+ } else {
1753
+ return JSON.stringify({ error: "Cloud mesh clone_node not yet implemented" });
1754
+ }
1755
+ }
1756
+ async function meshRemoveNode(ctx, args) {
1757
+ const node = await findNodeWithRefresh(ctx, args.node_id);
1758
+ if (isLocalTransport(ctx.transport)) {
1759
+ const result = await commandForNode(ctx, node, "remove_mesh_node", {
1760
+ meshId: ctx.mesh.id,
1761
+ nodeId: args.node_id,
1762
+ inlineMesh: ctx.mesh
1763
+ });
1764
+ if (result?.success && result.removed !== false) {
1765
+ const idx = ctx.mesh.nodes.findIndex((n) => n.id === args.node_id);
1766
+ if (idx >= 0) {
1767
+ ctx.mesh.nodes.splice(idx, 1);
1768
+ ctx.mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1769
+ }
1770
+ }
1771
+ return JSON.stringify(result, null, 2);
1772
+ } else {
1773
+ return JSON.stringify({ error: "Cloud mesh remove_node not yet implemented" });
1774
+ }
1775
+ }
1527
1776
 
1528
1777
  // src/server.ts
1778
+ async function buildMeshModeCoordinatorPrompt(mesh) {
1779
+ try {
1780
+ const { buildCoordinatorSystemPrompt } = await import("@adhdev/daemon-core");
1781
+ return buildCoordinatorSystemPrompt({ mesh });
1782
+ } catch (e) {
1783
+ throw new Error(`Failed to build Repo Mesh coordinator prompt: ${e?.message ?? String(e)}`);
1784
+ }
1785
+ }
1529
1786
  async function startMcpServer(opts) {
1530
- const transport = opts.mode === "cloud" ? new CloudTransport({ apiKey: opts.apiKey, baseUrl: opts.baseUrl }) : new LocalTransport({ port: opts.port, password: opts.password });
1787
+ const transport = opts.mode === "cloud" ? new CloudTransport({ apiKey: opts.apiKey, baseUrl: opts.baseUrl }) : opts.mode === "ipc" ? new IpcTransport({ port: opts.port }) : new LocalTransport({ port: opts.port, password: opts.password });
1531
1788
  const alive = await transport.ping();
1532
1789
  if (!alive) {
1533
- const hint = opts.mode === "local" ? `Make sure the standalone daemon is running (adhdev standalone or npx @adhdev/daemon-standalone).` : `Check your API key and network connectivity.`;
1790
+ const hint = opts.mode === "local" ? `Make sure the standalone daemon is running (adhdev standalone or npx @adhdev/daemon-standalone).` : opts.mode === "ipc" ? `Make sure the cloud daemon is running with local IPC enabled (adhdev daemon).` : `Check your API key and network connectivity.`;
1534
1791
  process.stderr.write(`[adhdev-mcp] Cannot reach ${opts.mode} daemon. ${hint}
1535
1792
  `);
1536
1793
  process.exit(1);
@@ -1613,7 +1870,7 @@ async function startMcpServer(opts) {
1613
1870
  `);
1614
1871
  }
1615
1872
  }
1616
- if (!mesh && transport instanceof LocalTransport) {
1873
+ if (!mesh && (transport instanceof LocalTransport || transport instanceof IpcTransport)) {
1617
1874
  try {
1618
1875
  const result = await transport.command("get_mesh", { meshId: opts.meshId });
1619
1876
  if (result?.success && result.mesh) {
@@ -1632,13 +1889,7 @@ async function startMcpServer(opts) {
1632
1889
  process.exit(1);
1633
1890
  }
1634
1891
  const meshCtx = { mesh, transport };
1635
- let coordinatorPrompt = "";
1636
- try {
1637
- const { buildCoordinatorSystemPrompt } = await import("@adhdev/daemon-core");
1638
- coordinatorPrompt = buildCoordinatorSystemPrompt({ mesh });
1639
- } catch {
1640
- coordinatorPrompt = `You are a Repo Mesh Coordinator for "${mesh.name}" (${mesh.repoIdentity}). Use mesh_* tools to orchestrate work.`;
1641
- }
1892
+ const coordinatorPrompt = await buildMeshModeCoordinatorPrompt(mesh);
1642
1893
  const server2 = new import_server.Server(
1643
1894
  { name: "adhdev-mcp-server", version: "0.9.75" },
1644
1895
  { capabilities: { tools: {}, resources: {} } }
@@ -1689,6 +1940,12 @@ async function startMcpServer(opts) {
1689
1940
  case "mesh_approve":
1690
1941
  text = await meshApprove(meshCtx, a);
1691
1942
  break;
1943
+ case "mesh_clone_node":
1944
+ text = await meshCloneNode(meshCtx, a);
1945
+ break;
1946
+ case "mesh_remove_node":
1947
+ text = await meshRemoveNode(meshCtx, a);
1948
+ break;
1692
1949
  default:
1693
1950
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
1694
1951
  }
@@ -1817,13 +2074,14 @@ async function startMcpServer(opts) {
1817
2074
  }
1818
2075
 
1819
2076
  // src/index.ts
1820
- function parseArgs(argv) {
2077
+ function parseArgs(argv, env = process.env) {
1821
2078
  const args = argv.slice(2);
1822
2079
  let apiKey;
1823
2080
  let baseUrl;
1824
2081
  let port;
1825
2082
  let password;
1826
2083
  let meshId;
2084
+ let explicitMode;
1827
2085
  for (let i = 0; i < args.length; i++) {
1828
2086
  const arg = args[i];
1829
2087
  if ((arg === "--api-key" || arg === "-k") && args[i + 1]) {
@@ -1832,6 +2090,12 @@ function parseArgs(argv) {
1832
2090
  apiKey = arg.slice("--api-key=".length);
1833
2091
  } else if (arg === "--base-url" && args[i + 1]) {
1834
2092
  baseUrl = args[++i];
2093
+ } else if (arg === "--mode" && args[i + 1]) {
2094
+ const value = String(args[++i]).trim();
2095
+ if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
2096
+ } else if (arg?.startsWith("--mode=")) {
2097
+ const value = arg.slice("--mode=".length).trim();
2098
+ if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
1835
2099
  } else if (arg === "--port" && args[i + 1]) {
1836
2100
  port = Number(args[++i]);
1837
2101
  } else if (arg?.startsWith("--port=")) {
@@ -1847,10 +2111,14 @@ function parseArgs(argv) {
1847
2111
  process.exit(0);
1848
2112
  }
1849
2113
  }
1850
- if (!apiKey && process.env.ADHDEV_API_KEY) apiKey = process.env.ADHDEV_API_KEY;
1851
- if (!password && process.env.ADHDEV_PASSWORD) password = process.env.ADHDEV_PASSWORD;
1852
- if (!meshId && process.env.ADHDEV_MESH_ID) meshId = process.env.ADHDEV_MESH_ID;
1853
- const mode = apiKey ? "cloud" : "local";
2114
+ if (!apiKey && env.ADHDEV_API_KEY) apiKey = env.ADHDEV_API_KEY;
2115
+ if (!password && env.ADHDEV_PASSWORD) password = env.ADHDEV_PASSWORD;
2116
+ if (!meshId && env.ADHDEV_MESH_ID) meshId = env.ADHDEV_MESH_ID;
2117
+ if (!explicitMode && env.ADHDEV_MCP_TRANSPORT) {
2118
+ const value = env.ADHDEV_MCP_TRANSPORT.trim();
2119
+ if (value === "local" || value === "cloud" || value === "ipc") explicitMode = value;
2120
+ }
2121
+ const mode = explicitMode || (apiKey ? "cloud" : meshId && env.ADHDEV_INLINE_MESH ? "ipc" : "local");
1854
2122
  return { mode, port, password, apiKey, baseUrl, meshId };
1855
2123
  }
1856
2124
  function printHelp() {
@@ -1860,10 +2128,12 @@ adhdev-mcp \u2014 ADHDev MCP Server
1860
2128
  Usage:
1861
2129
  adhdev-mcp Local mode (requires standalone daemon)
1862
2130
  adhdev-mcp --api-key <key> Cloud mode (ADHDev cloud API)
2131
+ adhdev-mcp --mode ipc --repo-mesh <mesh_id> Cloud daemon IPC mesh mode
1863
2132
  adhdev-mcp --repo-mesh <mesh_id> Mesh mode (coordinator-scoped tools)
1864
2133
 
1865
2134
  Options:
1866
- --port <n> Standalone daemon port (default: 3847)
2135
+ --mode <mode> Transport: local, cloud, or ipc
2136
+ --port <n> Standalone or IPC daemon port (defaults: local 3847, ipc 19222)
1867
2137
  --password <pass> Standalone daemon password (if set)
1868
2138
  --api-key <key> ADHDev cloud API key (switches to cloud mode)
1869
2139
  --base-url <url> Override cloud API base URL
@@ -1874,9 +2144,10 @@ Environment variables:
1874
2144
  ADHDEV_API_KEY API key (cloud mode)
1875
2145
  ADHDEV_PASSWORD Daemon password (local mode)
1876
2146
  ADHDEV_MESH_ID Mesh ID (mesh mode)
2147
+ ADHDEV_MCP_TRANSPORT Transport: local, cloud, or ipc
1877
2148
 
1878
2149
  Standard tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status, git_log, git_diff, git_checkpoint, git_push, screenshot
1879
- Mesh tools: mesh_status, mesh_list_nodes, mesh_send_task, mesh_read_chat, mesh_launch_session, mesh_git_status, mesh_checkpoint, mesh_approve
2150
+ Mesh tools: mesh_status, mesh_list_nodes, mesh_send_task, mesh_read_chat, mesh_launch_session, mesh_git_status, mesh_checkpoint, mesh_approve, mesh_clone_node, mesh_remove_node
1880
2151
  `.trim());
1881
2152
  }
1882
2153
  startMcpServer(parseArgs(process.argv)).catch((err) => {
@@ -1884,4 +2155,8 @@ startMcpServer(parseArgs(process.argv)).catch((err) => {
1884
2155
  `);
1885
2156
  process.exit(1);
1886
2157
  });
2158
+ // Annotate the CommonJS export names for ESM import in node:
2159
+ 0 && (module.exports = {
2160
+ parseArgs
2161
+ });
1887
2162
  //# sourceMappingURL=index.js.map