clawmatrix 0.5.1 → 0.6.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.
@@ -645,27 +645,53 @@ async function cmdApprovalRevoke(nodeId) {
645
645
 
646
646
  function cmdHelpJson() {
647
647
  const commands = [
648
+ // Cluster
648
649
  { name: "status", args: "", options: ["--json"], description: "Show cluster topology and peer status" },
649
650
  { name: "peers", args: "", options: [], description: "List known peers (JSON)" },
650
651
  { name: "check", args: "<nodeId>", options: [], description: "Check if a specific node is reachable" },
652
+ { name: "config", args: "", options: ["--json"], description: "Show local node configuration" },
653
+ { name: "availability", args: "[range]", options: ["--json"], description: "Show node uptime (24h|7d|90d)" },
654
+ // Tools & Execution
651
655
  { name: "tools", args: "[node]", options: ["--json", "-v", "-f <keyword>", "-d <tool>"], description: "List available tools on remote nodes" },
652
656
  { name: "call", args: "<node> <tool> [json]", options: ["-t <ms>"], description: "Invoke a tool on a remote node" },
653
657
  { name: "batch", args: "<node> [json]", options: ["--no-stop-on-error", "-t <ms>"], description: "Invoke multiple tools in sequence" },
654
658
  { name: "models", args: "", options: ["--json", "-n <nodeId>"], description: "List all cluster models" },
655
- { name: "events", args: "", options: ["--json", "-t <type>", "-s <source>", "-l <n>", "-a", "--consume <ids>"], description: "Query and consume events" },
659
+ // Agents
656
660
  { name: "send", args: "<node> <message>", options: [], description: "Send a message to a remote node" },
657
661
  { name: "handoff", args: "<task>", options: ["--agent <agent>", "--node <node>", "-t <ms>"], description: "Delegate a task to a remote agent" },
658
- { name: "acp", args: "<action>", options: ["-n <node>", "-a <agent>", "--session <id>", "--mode <mode>", "--cwd <dir>"], description: "Manage ACP agent sessions" },
662
+ { name: "acp", args: "<action>", options: ["-n <node>", "-a <agent>", "--session <id>", "--mode <mode>", "--cwd <dir>"], description: "Manage ACP agent sessions (list|prompt|resume|cancel|close)" },
663
+ // Events & Automations
664
+ { name: "events", args: "", options: ["--json", "-t <type>", "-s <source>", "-l <n>", "-a", "--consume <ids>"], description: "Query and consume events" },
665
+ { name: "events ingest", args: "<json>", options: [], description: "Ingest events from external sources" },
666
+ { name: "automations rules", args: "", options: ["--json"], description: "List automation rules" },
667
+ { name: "automations history", args: "", options: ["--json", "--limit <n>"], description: "Show automation execution history" },
668
+ { name: "automations run", args: "<ruleId>", options: ["--json"], description: "Manually trigger an automation rule" },
669
+ { name: "automations replay", args: "<executionId>", options: ["--json"], description: "Re-run a historical execution" },
670
+ { name: "automations save", args: "<json-file|->", options: [], description: "Save automation rules from JSON" },
671
+ // Infrastructure
659
672
  { name: "diagnostic", args: "<node>", options: ["--action status|exec", "-c <command>", "-t <seconds>"], description: "Diagnose a remote node via sentinel" },
660
673
  { name: "notify", args: "<title>", options: ["--detail <text>", "--progress <0-100>", "--action start|update|end", "--task-id <id>", "--tool <name>"], description: "Push notification to mobile devices" },
661
674
  { name: "terminal", args: "<node>", options: ["--shell <shell>", "--cwd <dir>", "--action list|close|read|input", "--session <id>"], description: "Open/manage remote terminal sessions" },
662
675
  { name: "transfer", args: "<node> <path> [remote]", options: ["--pull", "-t <ms>"], description: "Transfer files to/from a remote node" },
676
+ // Knowledge & Kanban
677
+ { name: "kb files", args: "", options: ["--json"], description: "List all synced knowledge files" },
678
+ { name: "kb history", args: "<path>", options: ["--json"], description: "Show change history for a synced file" },
679
+ { name: "kb blame", args: "<path>", options: ["--json"], description: "Show line-by-line attribution for a synced file" },
680
+ { name: "board", args: "", options: ["--json"], description: "Kanban board summary" },
681
+ { name: "board list", args: "", options: ["--json", "--stage <s>", "--label <l>", "--priority <p>", "--node <n>"], description: "List kanban cards" },
682
+ { name: "board create", args: "<title>", options: ["--desc <text>", "--priority <p>", "--labels <l1,l2>", "--node <n>", "--agent <a>"], description: "Create a new kanban card" },
683
+ { name: "board get", args: "<cardId>", options: ["--json"], description: "Get kanban card details" },
684
+ { name: "board update", args: "<cardId>", options: ["--title <t>", "--desc <d>", "--priority <p>", "--labels <l1,l2>"], description: "Update kanban card properties" },
685
+ { name: "board claim", args: "<cardId>", options: ["--agent <a>"], description: "Claim a kanban card" },
686
+ { name: "board move", args: "<cardId> <stage>", options: [], description: "Move kanban card to a stage" },
687
+ { name: "board annotate", args: "<cardId> <text>", options: ["--type <t>"], description: "Add annotation to a kanban card" },
688
+ { name: "board delete", args: "<cardId>", options: [], description: "Delete a kanban card" },
689
+ // Access Control
663
690
  { name: "approve", args: "<approvalId>", options: [], description: "Approve a pending peer" },
664
691
  { name: "deny", args: "<approvalId>", options: [], description: "Deny a pending peer" },
665
692
  { name: "approval list", args: "", options: [], description: "List approved/pending peers" },
666
693
  { name: "approval revoke", args: "<nodeId>", options: [], description: "Revoke an approved peer" },
667
- { name: "kb files", args: "", options: ["--json"], description: "List all synced knowledge files" },
668
- { name: "kb history", args: "<path>", options: ["--json"], description: "Show change history for a synced file" },
694
+ // Other
669
695
  { name: "install-skill", args: "", options: [], description: "Symlink skill into ~/.claude/skills/" },
670
696
  ];
671
697
  console.log(jsonOut({ prefix: "clawmatrix", commands }));
@@ -945,7 +971,8 @@ async function cmdKb(args) {
945
971
 
946
972
  Subcommands:
947
973
  files [--json] List all synced files with metadata
948
- history <path> [--json] Show change history for a synced file`);
974
+ history <path> [--json] Show change history for a synced file
975
+ blame <path> [--json] Show line-by-line attribution for a synced file`);
949
976
  return;
950
977
  }
951
978
 
@@ -1016,6 +1043,44 @@ Subcommands:
1016
1043
  return;
1017
1044
  }
1018
1045
 
1046
+ if (subCmd === "blame") {
1047
+ const filePath = subArgs.filter((a) => !a.startsWith("--"))[0];
1048
+ if (!filePath) { console.error("Usage: clawmatrix kb blame <path>"); process.exit(1); }
1049
+ const result = await callGateway("clawmatrix.kb.blame", { path: filePath });
1050
+ if (isJson) { console.log(jsonOut(result)); return; }
1051
+
1052
+ const blame = result?.blame;
1053
+ if (!Array.isArray(blame) || blame.length === 0) {
1054
+ console.log(`No blame data for: ${filePath}`);
1055
+ return;
1056
+ }
1057
+
1058
+ if (!isTTY) {
1059
+ for (const b of blame) {
1060
+ const parts = [b.line !== undefined ? `L${b.line}` : "?"];
1061
+ if (b.nodeId) parts.push(`node=${b.nodeId}`);
1062
+ if (b.agentId) parts.push(`agent=${b.agentId}`);
1063
+ parts.push(`actor=${b.actor}`);
1064
+ parts.push(new Date(b.timestamp).toISOString());
1065
+ parts.push(b.content || "");
1066
+ console.log(parts.join(" | "));
1067
+ }
1068
+ return;
1069
+ }
1070
+
1071
+ console.log();
1072
+ console.log(` ${cyan("◆")} ${bold("File Blame")} ${dim(filePath)}`);
1073
+ console.log(` ${bar}`);
1074
+ for (const b of blame) {
1075
+ const lineNum = b.line !== undefined ? dim(`L${String(b.line).padStart(4)}`) : dim(" ?");
1076
+ const attr = b.nodeId ? yellow(b.nodeId) : dim(b.actor || "?");
1077
+ const content = b.content || "";
1078
+ console.log(` ${bar} ${lineNum} ${attr} ${content}`);
1079
+ }
1080
+ console.log();
1081
+ return;
1082
+ }
1083
+
1019
1084
  console.error(`Unknown kb subcommand: ${subCmd}`);
1020
1085
  process.exit(1);
1021
1086
  } catch (e) {
@@ -1041,6 +1106,8 @@ Subcommands:
1041
1106
  create <title> [--desc <text>] [--priority <p>] [--labels <l1,l2>]
1042
1107
  Create a new card
1043
1108
  get <cardId> Get card details
1109
+ update <cardId> [--title <t>] [--desc <d>] [--priority <p>] [--labels <l1,l2>]
1110
+ Update card properties
1044
1111
  claim <cardId> [--agent <a>] Claim a backlog card
1045
1112
  move <cardId> <stage> Move card to a stage (backlog|claimed|in_progress|review|done|archived)
1046
1113
  annotate <cardId> <text> [--type <t>]
@@ -1150,6 +1217,29 @@ Subcommands:
1150
1217
  return;
1151
1218
  }
1152
1219
 
1220
+ if (subCmd === "update" || subCmd === "edit") {
1221
+ const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
1222
+ if (!cardId) { console.error("Usage: clawmatrix board update <cardId> [--title <t>] [--desc <d>] [--priority <p>] [--labels <l1,l2>]"); process.exit(1); }
1223
+ const params = { cardId };
1224
+ const titleIdx = subArgs.indexOf("--title");
1225
+ if (titleIdx !== -1) params.title = subArgs[titleIdx + 1];
1226
+ const descIdx = subArgs.indexOf("--desc");
1227
+ if (descIdx !== -1) params.description = subArgs[descIdx + 1];
1228
+ const prioIdx = subArgs.indexOf("--priority");
1229
+ if (prioIdx !== -1) params.priority = subArgs[prioIdx + 1];
1230
+ const labelsIdx = subArgs.indexOf("--labels");
1231
+ if (labelsIdx !== -1) params.labels = subArgs[labelsIdx + 1]?.split(",");
1232
+ const nodeIdx = subArgs.indexOf("--node");
1233
+ if (nodeIdx !== -1) params.targetNode = subArgs[nodeIdx + 1];
1234
+ const agentIdx = subArgs.indexOf("--agent");
1235
+ if (agentIdx !== -1) params.targetAgent = subArgs[agentIdx + 1];
1236
+
1237
+ const card = await callGateway("clawmatrix.board.update", params);
1238
+ if (isJson) { console.log(jsonOut(card)); return; }
1239
+ console.log(`Updated: ${card.id} — ${card.title}`);
1240
+ return;
1241
+ }
1242
+
1153
1243
  if (subCmd === "claim") {
1154
1244
  const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
1155
1245
  if (!cardId) { console.error("Usage: clawmatrix board claim <cardId>"); process.exit(1); }
@@ -1226,35 +1316,338 @@ Subcommands:
1226
1316
  }
1227
1317
  }
1228
1318
 
1319
+ // ── Availability command ──────────────────────────────────────────
1320
+
1321
+ async function cmdAvailability(args) {
1322
+ const isJson = args.includes("--json");
1323
+ const range = args.find((a) => ["24h", "7d", "90d"].includes(a)) || "24h";
1324
+
1325
+ try {
1326
+ const data = await callGateway("clawmatrix.availability", { range });
1327
+ if (isJson) { console.log(jsonOut(data)); return; }
1328
+
1329
+ if (!isTTY) {
1330
+ console.log(`Range: ${data.range} | Bucket: ${data.bucketMinutes}min`);
1331
+ for (const node of (data.nodes || [])) {
1332
+ const uptimeStr = node.uptimeRatio !== undefined ? `${(node.uptimeRatio * 100).toFixed(1)}%` : "N/A";
1333
+ console.log(`${node.nodeId} | uptime: ${uptimeStr} | buckets: ${node.buckets?.length ?? 0}`);
1334
+ }
1335
+ return;
1336
+ }
1337
+
1338
+ console.log();
1339
+ console.log(` ${cyan("◆")} ${bold("Availability")} ${dim(`range: ${data.range}, bucket: ${data.bucketMinutes}min`)}`);
1340
+ console.log(` ${bar}`);
1341
+
1342
+ for (const node of (data.nodes || [])) {
1343
+ const uptimeStr = node.uptimeRatio !== undefined ? `${(node.uptimeRatio * 100).toFixed(1)}%` : "N/A";
1344
+ const uptimeColor = node.uptimeRatio >= 0.99 ? green : node.uptimeRatio >= 0.9 ? yellow : red;
1345
+ console.log(` ${bar} ${bold(node.nodeId)} ${lbl("uptime")}${uptimeColor(uptimeStr)}`);
1346
+
1347
+ if (node.buckets?.length > 0) {
1348
+ // Render a compact timeline bar
1349
+ const chars = node.buckets.map((b) => {
1350
+ if (b.status === "up") return green("▓");
1351
+ if (b.status === "degraded") return yellow("▒");
1352
+ if (b.status === "down") return red("░");
1353
+ return dim("·");
1354
+ });
1355
+ // Show at most 60 chars for readability
1356
+ const maxWidth = 60;
1357
+ const step = Math.max(1, Math.ceil(chars.length / maxWidth));
1358
+ const compressed = [];
1359
+ for (let i = 0; i < chars.length; i += step) {
1360
+ compressed.push(chars[i]);
1361
+ }
1362
+ console.log(` ${bar} ${compressed.join("")}`);
1363
+ }
1364
+ }
1365
+
1366
+ if (data.gaps?.length > 0) {
1367
+ console.log(` ${bar}`);
1368
+ console.log(` ${bar} ${bold("Gaps")}`);
1369
+ for (const gap of data.gaps.slice(0, 10)) {
1370
+ const from = new Date(gap.from).toLocaleString();
1371
+ const to = new Date(gap.to).toLocaleString();
1372
+ const dur = Math.round((gap.to - gap.from) / 60000);
1373
+ console.log(` ${bar} ${gap.nodeId} ${dim(from)} → ${dim(to)} ${red(`${dur}min`)}`);
1374
+ }
1375
+ }
1376
+ console.log();
1377
+ } catch (err) {
1378
+ console.error(`Error: ${err.message || String(err)}`);
1379
+ process.exitCode = 1;
1380
+ }
1381
+ }
1382
+
1383
+ // ── Automations command ──────────────────────────────────────────
1384
+
1385
+ async function cmdAutomations(args) {
1386
+ const isJson = args.includes("--json");
1387
+ const subCmd = args[0];
1388
+ const subArgs = args.slice(1);
1389
+
1390
+ if (!subCmd || subCmd === "--help" || subCmd === "-h") {
1391
+ console.log(`Usage: clawmatrix automations [subcommand]
1392
+
1393
+ Subcommands:
1394
+ rules [--json] List all automation rules
1395
+ history [--limit <n>] [--json]
1396
+ Show execution history
1397
+ run <ruleId> [--json] Manually trigger a rule
1398
+ replay <executionId> [--json]
1399
+ Re-run a historical execution
1400
+ save <json-file|-> Save rules from JSON file or stdin`);
1401
+ return;
1402
+ }
1403
+
1404
+ try {
1405
+ if (subCmd === "rules" || subCmd === "list") {
1406
+ const data = await callGateway("clawmatrix.automations.rules");
1407
+ if (isJson) { console.log(jsonOut(data)); return; }
1408
+ const rules = data.rules || [];
1409
+ if (rules.length === 0) { console.log("No automation rules configured."); return; }
1410
+
1411
+ if (!isTTY) {
1412
+ for (const r of rules) {
1413
+ const status = r.enabled ? "enabled" : "disabled";
1414
+ const trigger = r.trigger?.type || "unknown";
1415
+ console.log(`${r.id} | ${status} | trigger:${trigger} | ${r.name || "(unnamed)"}`);
1416
+ }
1417
+ return;
1418
+ }
1419
+
1420
+ console.log();
1421
+ console.log(` ${cyan("◆")} ${bold("Automation Rules")} ${dim(`${rules.length} rule(s)`)}`);
1422
+ console.log(` ${bar}`);
1423
+ for (const r of rules) {
1424
+ const dot = r.enabled ? green("●") : red("○");
1425
+ const trigger = r.trigger?.type || "unknown";
1426
+ console.log(` ${bar} ${dot} ${bold(r.id)} ${r.name || dim("(unnamed)")} ${dim(`trigger: ${trigger}`)}`);
1427
+ }
1428
+ console.log();
1429
+ return;
1430
+ }
1431
+
1432
+ if (subCmd === "history") {
1433
+ const lIdx = subArgs.indexOf("--limit") !== -1 ? subArgs.indexOf("--limit") : subArgs.indexOf("-l");
1434
+ const limit = lIdx !== -1 ? parseInt(subArgs[lIdx + 1], 10) : 20;
1435
+ const data = await callGateway("clawmatrix.automations.history", { limit });
1436
+ if (isJson) { console.log(jsonOut(data)); return; }
1437
+ const executions = data.executions || [];
1438
+ if (executions.length === 0) { console.log("No execution history."); return; }
1439
+
1440
+ if (!isTTY) {
1441
+ for (const ex of executions) {
1442
+ const ts = new Date(ex.startedAt || ex.ts).toISOString();
1443
+ const dur = ex.durationMs ? `${ex.durationMs}ms` : "?";
1444
+ console.log(`${ts} | ${ex.ruleId} | ${ex.status} | ${dur}`);
1445
+ }
1446
+ return;
1447
+ }
1448
+
1449
+ console.log();
1450
+ console.log(` ${cyan("◆")} ${bold("Execution History")} ${dim(`${executions.length} execution(s)`)}`);
1451
+ console.log(` ${bar}`);
1452
+ for (const ex of executions) {
1453
+ const ts = new Date(ex.startedAt || ex.ts).toLocaleString();
1454
+ const dur = ex.durationMs ? dim(`${ex.durationMs}ms`) : "";
1455
+ const statusColor = ex.status === "success" ? green : ex.status === "error" ? red : yellow;
1456
+ console.log(` ${bar} ${statusColor("●")} ${bold(ex.ruleId)} ${statusColor(ex.status)} ${dur} ${dim(ts)}`);
1457
+ if (ex.error) console.log(` ${bar} ${red(ex.error)}`);
1458
+ }
1459
+ console.log();
1460
+ return;
1461
+ }
1462
+
1463
+ if (subCmd === "run") {
1464
+ const ruleId = subArgs.filter((a) => !a.startsWith("--"))[0];
1465
+ if (!ruleId) { console.error("Usage: clawmatrix automations run <ruleId>"); process.exit(1); }
1466
+ const data = await callGateway("clawmatrix.automations.run", { ruleId }, 60000);
1467
+ if (isJson) { console.log(jsonOut(data)); return; }
1468
+ const ex = data.execution;
1469
+ const statusColor = ex?.status === "success" ? green : red;
1470
+ console.log(`${statusColor("●")} Rule ${ruleId}: ${statusColor(ex?.status || "unknown")}${ex?.durationMs ? ` (${ex.durationMs}ms)` : ""}`);
1471
+ if (ex?.error) console.log(` ${red(ex.error)}`);
1472
+ return;
1473
+ }
1474
+
1475
+ if (subCmd === "replay") {
1476
+ const executionId = subArgs.filter((a) => !a.startsWith("--"))[0];
1477
+ if (!executionId) { console.error("Usage: clawmatrix automations replay <executionId>"); process.exit(1); }
1478
+ const data = await callGateway("clawmatrix.automations.replay", { executionId }, 60000);
1479
+ if (isJson) { console.log(jsonOut(data)); return; }
1480
+ console.log(`Replayed: ${executionId} → ${data.execution?.status || "unknown"}`);
1481
+ return;
1482
+ }
1483
+
1484
+ if (subCmd === "save") {
1485
+ let jsonStr = subArgs.filter((a) => !a.startsWith("--"))[0];
1486
+ if (jsonStr === "-" || (!jsonStr && !process.stdin.isTTY)) {
1487
+ const chunks = [];
1488
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
1489
+ jsonStr = Buffer.concat(chunks).toString("utf-8").trim();
1490
+ } else if (jsonStr && !jsonStr.startsWith("[")) {
1491
+ // Treat as file path
1492
+ try { jsonStr = readFileSync(resolve(jsonStr), "utf-8"); } catch (e) {
1493
+ console.error(`Error: Could not read file: ${jsonStr}`);
1494
+ process.exit(1);
1495
+ }
1496
+ }
1497
+ if (!jsonStr) { console.error("Usage: clawmatrix automations save <json-file|->"); process.exit(1); }
1498
+ const rules = JSON.parse(jsonStr);
1499
+ const data = await callGateway("clawmatrix.automations.save", { rules });
1500
+ console.log(`Saved ${data.count} rule(s)`);
1501
+ return;
1502
+ }
1503
+
1504
+ console.error(`Unknown automations subcommand: ${subCmd}`);
1505
+ process.exit(1);
1506
+ } catch (err) {
1507
+ console.error(`Error: ${err.message || String(err)}`);
1508
+ process.exitCode = 1;
1509
+ }
1510
+ }
1511
+
1512
+ // ── Config command ───────────────────────────────────────────────
1513
+
1514
+ async function cmdConfig(args) {
1515
+ const isJson = args.includes("--json");
1516
+
1517
+ try {
1518
+ const data = await callGateway("clawmatrix.config.get");
1519
+ if (isJson) { console.log(jsonOut(data)); return; }
1520
+
1521
+ if (!isTTY) {
1522
+ const parts = [`nodeId: ${data.nodeId}`];
1523
+ parts.push(`listen: ${data.listen !== false ? data.listen : "disabled"}`);
1524
+ parts.push(`e2ee: ${data.e2ee ?? false}`);
1525
+ parts.push(`tags: ${(data.tags || []).join(",") || "none"}`);
1526
+ parts.push(`agents: ${(data.agents || []).map((a) => a.id).join(",") || "none"}`);
1527
+ parts.push(`models: ${(data.models || []).map((m) => m.id).join(",") || "none"}`);
1528
+ parts.push(`toolProxy: ${data.toolProxy?.enabled ? "enabled" : "disabled"}`);
1529
+ parts.push(`terminal: ${data.terminal?.enabled ? "enabled" : "disabled"}`);
1530
+ parts.push(`acp: ${data.acp?.enabled ? "enabled" : "disabled"}`);
1531
+ parts.push(`knowledge: ${data.knowledge?.enabled ? "enabled" : "disabled"}`);
1532
+ parts.push(`proxyModels: ${data.proxyModels}`);
1533
+ parts.push(`peers: ${data.peers}`);
1534
+ console.log(parts.join(" | "));
1535
+ return;
1536
+ }
1537
+
1538
+ console.log();
1539
+ console.log(` ${cyan("◆")} ${bold("ClawMatrix Config")}`);
1540
+ console.log(` ${bar}`);
1541
+ console.log(` ${bar} ${lbl("Node ID")}${bold(String(data.nodeId))}`);
1542
+ console.log(` ${bar} ${lbl("Listen")}${data.listen !== false ? `:${data.listen}` : dim("disabled")}`);
1543
+ console.log(` ${bar} ${lbl("E2EE")}${data.e2ee ? green("enabled") : dim("disabled")}`);
1544
+ console.log(` ${bar} ${lbl("Tags")}${(data.tags || []).join(dim(", ")) || dim("none")}`);
1545
+ console.log(` ${bar}`);
1546
+ console.log(` ${bar} ${bold("Agents")}`);
1547
+ for (const a of (data.agents || [])) {
1548
+ console.log(` ${bar} ${green("●")} ${a.id} ${dim(a.model || "")}`);
1549
+ }
1550
+ if ((data.agents || []).length === 0) console.log(` ${bar} ${dim("none")}`);
1551
+ console.log(` ${bar}`);
1552
+ console.log(` ${bar} ${bold("Models")}`);
1553
+ for (const m of (data.models || [])) {
1554
+ console.log(` ${bar} ${green("●")} ${m.id} ${dim(m.provider || "")}`);
1555
+ }
1556
+ if ((data.models || []).length === 0) console.log(` ${bar} ${dim("none")}`);
1557
+ console.log(` ${bar}`);
1558
+ console.log(` ${bar} ${bold("Features")}`);
1559
+ const feat = (name, enabled) => ` ${bar} ${enabled ? green("●") : red("○")} ${name}`;
1560
+ console.log(feat("Tool Proxy", data.toolProxy?.enabled));
1561
+ if (data.toolProxy?.enabled) {
1562
+ const allow = data.toolProxy.allow?.join(", ") || "*";
1563
+ const deny = data.toolProxy.deny?.length > 0 ? `, deny: ${data.toolProxy.deny.join(", ")}` : "";
1564
+ console.log(` ${bar} ${dim(`allow: ${allow}${deny}`)}`);
1565
+ }
1566
+ console.log(feat("Terminal", data.terminal?.enabled));
1567
+ console.log(feat("ACP", data.acp?.enabled));
1568
+ console.log(feat("Knowledge Sync", data.knowledge?.enabled));
1569
+ console.log(` ${bar}`);
1570
+ console.log(` ${bar} ${lbl("Proxy Models")}${data.proxyModels}`);
1571
+ console.log(` ${bar} ${lbl("Peers")}${data.peers}`);
1572
+ console.log();
1573
+ } catch (err) {
1574
+ console.error(`Error: ${err.message || String(err)}`);
1575
+ process.exitCode = 1;
1576
+ }
1577
+ }
1578
+
1579
+ // ── Events ingest subcommand ─────────────────────────────────────
1580
+
1581
+ async function cmdEventsIngest(args) {
1582
+ let jsonStr = args.find((a) => a.startsWith("[") || a.startsWith("{"));
1583
+ if (!jsonStr && !process.stdin.isTTY) {
1584
+ const chunks = [];
1585
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
1586
+ jsonStr = Buffer.concat(chunks).toString("utf-8").trim();
1587
+ }
1588
+ if (!jsonStr) {
1589
+ console.error('Usage: clawmatrix events ingest \'[{"type":"x","source":"y","data":{}}]\' or pipe via stdin');
1590
+ process.exit(1);
1591
+ }
1592
+ try {
1593
+ const raw = JSON.parse(jsonStr);
1594
+ const events = Array.isArray(raw) ? raw : [raw];
1595
+ const result = await callGateway("clawmatrix.events.ingest", { events });
1596
+ console.log(jsonOut(result));
1597
+ } catch (err) {
1598
+ console.error(`Error: ${err.message || String(err)}`);
1599
+ process.exitCode = 1;
1600
+ }
1601
+ }
1602
+
1229
1603
  const HELP = `ClawMatrix - Decentralized mesh cluster CLI
1230
1604
 
1231
1605
  Usage: clawmatrix <command> [options]
1232
1606
 
1233
- Commands:
1607
+ Cluster:
1234
1608
  status Show cluster topology and peer status
1609
+ peers List known peers (JSON)
1235
1610
  check <nodeId> Check if a specific node is reachable
1611
+ config Show local node configuration
1612
+ availability [range] Show node uptime (24h|7d|90d)
1613
+
1614
+ Tools & Execution:
1236
1615
  tools [node] List available tools on remote nodes
1237
1616
  call <node> <tool> [p] Invoke a tool on a remote node
1238
1617
  batch <node> [json] Invoke multiple tools in sequence
1239
1618
  models List all models available across the cluster
1240
- events Query and consume ingested events
1619
+
1620
+ Agents:
1241
1621
  send <node> <message> Send a message to a remote node
1242
1622
  handoff <task> [opts] Delegate a task to a remote agent
1243
- acp <action> [opts] Manage ACP agent sessions (list|prompt|resume|cancel|close)
1623
+ acp <action> [opts] ACP sessions (list|prompt|resume|cancel|close)
1624
+
1625
+ Events & Automations:
1626
+ events Query, consume, or ingest events
1627
+ automations [subcmd] Manage automation rules (rules|history|run|replay|save)
1628
+
1629
+ Infrastructure:
1244
1630
  diagnostic <node> Diagnose a remote node via sentinel
1245
- notify <title> [opts] Push notification to mobile devices (Dynamic Island)
1631
+ notify <title> [opts] Push notification to mobile (Dynamic Island)
1246
1632
  terminal <node> Open/manage remote terminal sessions
1247
1633
  transfer <node> <path> Transfer files to/from a remote node
1634
+
1635
+ Knowledge & Kanban:
1636
+ kb [subcommand] Knowledge sync (files|history|blame)
1637
+ board [subcommand] Kanban board (list|create|get|update|claim|move|annotate|delete)
1638
+
1639
+ Access Control:
1248
1640
  approve <approvalId> Approve a pending peer join request
1249
1641
  deny <approvalId> Deny a pending peer join request
1250
- kb [subcommand] Knowledge sync (files|history)
1251
- board [subcommand] Kanban board (list|create|get|claim|move|annotate|delete)
1252
1642
  approval list|revoke Manage approved peers
1643
+
1644
+ Other:
1253
1645
  install-skill Symlink skill into ~/.claude/skills/
1254
1646
  help-json Structured command reference (JSON)
1255
1647
 
1256
1648
  Options:
1257
1649
  --help, -h Show this help message
1650
+ --json Output as JSON (most commands)
1258
1651
 
1259
1652
  Run 'clawmatrix <command> --help' for command-specific options.`;
1260
1653
 
@@ -1273,7 +1666,10 @@ try {
1273
1666
  case "call": await cmdCall(rest[0], rest[1], rest[2], rest); break;
1274
1667
  case "batch": await cmdBatch(rest[0], rest.find((a) => a.startsWith("[") || a.startsWith("{")), rest); break;
1275
1668
  case "models": await cmdModels(rest); break;
1276
- case "events": await cmdEvents(rest); break;
1669
+ case "events":
1670
+ if (rest[0] === "ingest") await cmdEventsIngest(rest.slice(1));
1671
+ else await cmdEvents(rest);
1672
+ break;
1277
1673
  case "send": await cmdSend(rest[0], rest.slice(1).join(" ")); break;
1278
1674
  case "handoff": await cmdHandoff(rest); break;
1279
1675
  case "acp": await cmdAcp(rest[0], rest.slice(1)); break;
@@ -1283,6 +1679,9 @@ try {
1283
1679
  case "transfer": await cmdTransfer(rest[0], rest[1], rest.slice(2)); break;
1284
1680
  case "kb": case "knowledge": await cmdKb(rest); break;
1285
1681
  case "board": await cmdBoard(rest); break;
1682
+ case "availability": await cmdAvailability(rest); break;
1683
+ case "automations": case "automation": await cmdAutomations(rest); break;
1684
+ case "config": await cmdConfig(rest); break;
1286
1685
  case "approve": await cmdApprove(rest[0]); break;
1287
1686
  case "deny": await cmdDeny(rest[0]); break;
1288
1687
  case "approval":
@@ -18,7 +18,10 @@ All commands output LLM-friendly text (no ANSI colors) when stdout is not a TTY.
18
18
  ```bash
19
19
  clawmatrix status # Cluster topology: peers, agents, models, tags
20
20
  clawmatrix status --json # Structured JSON output
21
+ clawmatrix peers # List known peers (JSON)
21
22
  clawmatrix check <nodeId> # Quick reachability check with latency
23
+ clawmatrix config # Show local node configuration
24
+ clawmatrix config --json # Config as JSON
22
25
  ```
23
26
 
24
27
  Always start with `clawmatrix status` to understand the current topology before performing other operations.
@@ -55,6 +58,14 @@ clawmatrix models # All cluster models
55
58
  clawmatrix models --node <nodeId> # Filter by node
56
59
  ```
57
60
 
61
+ ## Availability
62
+
63
+ ```bash
64
+ clawmatrix availability # Node uptime (default: 24h)
65
+ clawmatrix availability 7d # 7-day uptime
66
+ clawmatrix availability 90d --json # 90-day as JSON
67
+ ```
68
+
58
69
  ## Events
59
70
 
60
71
  ```bash
@@ -63,6 +74,19 @@ clawmatrix events --type <type> # Filter by type
63
74
  clawmatrix events --source <nodeId> # Filter by source node
64
75
  clawmatrix events --consume <id1,id2> # Mark events as consumed
65
76
  clawmatrix events --all # Include consumed events
77
+ clawmatrix events ingest '[{"type":"x","source":"y","data":{}}]' # Ingest events
78
+ ```
79
+
80
+ ## Automations
81
+
82
+ ```bash
83
+ clawmatrix automations rules # List automation rules
84
+ clawmatrix automations history # Execution history
85
+ clawmatrix automations history --limit 5 # Limit results
86
+ clawmatrix automations run <ruleId> # Manually trigger a rule
87
+ clawmatrix automations replay <execId> # Re-run historical execution
88
+ clawmatrix automations save rules.json # Save rules from JSON file
89
+ cat rules.json | clawmatrix automations save - # Save from stdin
66
90
  ```
67
91
 
68
92
  ## Peer Approval
@@ -94,6 +118,31 @@ clawmatrix notify "iOS 构建" --action end --task-id <id>
94
118
 
95
119
  Options: `--detail <text>`, `--progress <0-100>`, `--action start|update|end`, `--task-id <id>`, `--tool <name>`
96
120
 
121
+ ## Knowledge Sync
122
+
123
+ ```bash
124
+ clawmatrix kb files # List synced knowledge files
125
+ clawmatrix kb history <path> # File change history
126
+ clawmatrix kb blame <path> # Line-by-line attribution
127
+ ```
128
+
129
+ ## Kanban Board
130
+
131
+ ```bash
132
+ clawmatrix board # Board summary
133
+ clawmatrix board list # List all cards
134
+ clawmatrix board list --stage in_progress --priority urgent # Filter cards
135
+ clawmatrix board create "Task title" --priority high --labels "bug,p0"
136
+ clawmatrix board get <cardId> # Card details
137
+ clawmatrix board update <cardId> --title "New title" --desc "Updated"
138
+ clawmatrix board claim <cardId> # Claim a card
139
+ clawmatrix board move <cardId> done # Move to stage
140
+ clawmatrix board annotate <cardId> "Progress note" --type progress
141
+ clawmatrix board delete <cardId> # Delete a card
142
+ ```
143
+
144
+ Stages: `backlog` → `claimed` → `in_progress` → `review` → `done` → `archived`
145
+
97
146
  ## Workflow
98
147
 
99
148
  1. Run `clawmatrix status` to see the cluster topology
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmatrix",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Decentralized mesh cluster plugin for OpenClaw — inter-gateway communication, model proxy, task handoff, and tool proxy.",
5
5
  "type": "module",
6
6
  "license": "MIT",