seatmesh 1.2.5 → 1.3.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.
Files changed (72) hide show
  1. package/bin/seatmesh +6 -1
  2. package/dist/commands/campaign-cli.d.ts +10 -0
  3. package/dist/commands/campaign-cli.d.ts.map +1 -0
  4. package/dist/commands/campaign-cli.js +136 -0
  5. package/dist/commands/campaign-cli.js.map +1 -0
  6. package/dist/commands/chat-cli.d.ts.map +1 -1
  7. package/dist/commands/chat-cli.js +55 -0
  8. package/dist/commands/chat-cli.js.map +1 -1
  9. package/dist/commands/completion.d.ts +3 -0
  10. package/dist/commands/completion.d.ts.map +1 -1
  11. package/dist/commands/completion.js +54 -13
  12. package/dist/commands/completion.js.map +1 -1
  13. package/dist/commands/completion.test.js +37 -2
  14. package/dist/commands/completion.test.js.map +1 -1
  15. package/dist/commands/contract-lock-cli.d.ts.map +1 -1
  16. package/dist/commands/contract-lock-cli.js +150 -29
  17. package/dist/commands/contract-lock-cli.js.map +1 -1
  18. package/dist/commands/help-text.d.ts.map +1 -1
  19. package/dist/commands/help-text.js +89 -25
  20. package/dist/commands/help-text.js.map +1 -1
  21. package/dist/commands/host-cli.d.ts +5 -0
  22. package/dist/commands/host-cli.d.ts.map +1 -0
  23. package/dist/commands/host-cli.js +120 -0
  24. package/dist/commands/host-cli.js.map +1 -0
  25. package/dist/commands/nav-cli.d.ts +8 -0
  26. package/dist/commands/nav-cli.d.ts.map +1 -0
  27. package/dist/commands/nav-cli.js +54 -0
  28. package/dist/commands/nav-cli.js.map +1 -0
  29. package/dist/commands/notify-cli.d.ts.map +1 -1
  30. package/dist/commands/notify-cli.js +38 -3
  31. package/dist/commands/notify-cli.js.map +1 -1
  32. package/dist/commands/room-cli.d.ts.map +1 -1
  33. package/dist/commands/room-cli.js +23 -6
  34. package/dist/commands/room-cli.js.map +1 -1
  35. package/dist/commands/schedule-cli.d.ts +13 -0
  36. package/dist/commands/schedule-cli.d.ts.map +1 -0
  37. package/dist/commands/schedule-cli.js +74 -0
  38. package/dist/commands/schedule-cli.js.map +1 -0
  39. package/dist/commands/schedule-cli.test.d.ts +2 -0
  40. package/dist/commands/schedule-cli.test.d.ts.map +1 -0
  41. package/dist/commands/schedule-cli.test.js +29 -0
  42. package/dist/commands/schedule-cli.test.js.map +1 -0
  43. package/dist/commands/todo-give.test.js +21 -5
  44. package/dist/commands/todo-give.test.js.map +1 -1
  45. package/dist/main.js +317 -39
  46. package/dist/main.js.map +1 -1
  47. package/dist/setup/init.d.ts +11 -0
  48. package/dist/setup/init.d.ts.map +1 -1
  49. package/dist/setup/init.js +38 -0
  50. package/dist/setup/init.js.map +1 -1
  51. package/dist/setup/init.test.js +3 -2
  52. package/dist/setup/init.test.js.map +1 -1
  53. package/dist/setup/install.js +2 -2
  54. package/dist/setup/install.js.map +1 -1
  55. package/package.json +6 -6
  56. package/profiles/minimal/mesh.config.yaml +1 -0
  57. package/templates/cursor/rules/seatmesh-agent.mdc +40 -0
  58. package/templates/docs/cli/README.md +110 -11
  59. package/templates/docs/cli/chat.md +4 -3
  60. package/templates/docs/cli/contract.md +15 -15
  61. package/templates/docs/cli/host.md +12 -0
  62. package/templates/docs/cli/human.md +15 -7
  63. package/templates/docs/cli/kind.md +4 -2
  64. package/templates/docs/cli/nav.md +12 -0
  65. package/templates/docs/cli/rebuild.md +3 -0
  66. package/templates/docs/cli/reload.md +8 -5
  67. package/templates/docs/cli/room.md +5 -7
  68. package/templates/docs/cli/schedule.md +13 -0
  69. package/templates/docs/cli/secretary.md +1 -1
  70. package/templates/docs/cli/spawn.md +13 -0
  71. package/templates/docs/cli/switch.md +4 -4
  72. package/templates/init/mesh.config.yaml +9 -1
@@ -1,7 +1,10 @@
1
- import { describe, expect, it } from "vitest";
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import { afterEach, describe, expect, it } from "vitest";
2
4
  import { spawnSync } from "node:child_process";
3
5
  import path from "node:path";
4
6
  import { fileURLToPath } from "node:url";
7
+ import { runInit } from "../setup/init.js";
5
8
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
6
9
  const bin = path.join(repoRoot, "packages/cli/bin/seatmesh");
7
10
  describe("todo give help surface", () => {
@@ -11,10 +14,23 @@ describe("todo give help surface", () => {
11
14
  expect(r.stdout).toMatch(/todo give/);
12
15
  expect(r.stdout).toMatch(/shorthand/i);
13
16
  });
14
- it("todo without args prints give-first usage", () => {
15
- const r = spawnSync(bin, ["todo"], { encoding: "utf8", cwd: repoRoot });
16
- expect(r.status).toBe(2);
17
- expect(r.stderr).toMatch(/todo give/);
17
+ describe("inside a mesh workspace", () => {
18
+ // `todo` (unlike `help`) requires a real .sm/ workspace (the shim gates
19
+ // non-allowlisted commands on `in_mesh_context`) — use an isolated tmp
20
+ // workspace rather than the repo's own dogfooded .sm/, so this passes in
21
+ // a clean checkout (e.g. the docker test runner) too.
22
+ let tmp = "";
23
+ afterEach(() => {
24
+ if (tmp)
25
+ fs.rmSync(tmp, { recursive: true, force: true });
26
+ });
27
+ it("todo without args prints give-first usage", () => {
28
+ tmp = fs.mkdtempSync(path.join(os.tmpdir(), "sm-todo-"));
29
+ runInit({ workspace: tmp, name: "todo-test" });
30
+ const r = spawnSync(bin, ["todo"], { encoding: "utf8", cwd: tmp });
31
+ expect(r.status).toBe(2);
32
+ expect(r.stderr).toMatch(/todo give/);
33
+ });
18
34
  });
19
35
  });
20
36
  //# sourceMappingURL=todo-give.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"todo-give.test.js","sourceRoot":"","sources":["../../src/commands/todo-give.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;AAC3F,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;AAE7D,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"todo-give.test.js","sourceRoot":"","sources":["../../src/commands/todo-give.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;AAC3F,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;AAE7D,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,wEAAwE;QACxE,uEAAuE;QACvE,yEAAyE;QACzE,sDAAsD;QACtD,IAAI,GAAG,GAAG,EAAE,CAAC;QAEb,SAAS,CAAC,GAAG,EAAE;YACb,IAAI,GAAG;gBAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;YACnD,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YAC/C,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/main.js CHANGED
@@ -1,23 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { findDotSmConfig } from "@seat-mesh/core";
4
- import { loadProfile, profilePaths, loadRoleIndex, renderRoleIndex, validateRoleIndex, runStackPassthrough, roleAllows, isCoordKind, isManagerKind, managerColumnIds, addBaseColumn, removeBaseColumn, listBaseColumns, chatRoomConfigForLoaded, clearAckEndedSync, resolveAckRedirectDefaults, } from "@seat-mesh/core";
4
+ import { loadProfile, profilePaths, loadRoleIndex, renderRoleIndex, validateRoleIndex, runStackPassthrough, roleAllows, isCoordKind, isManagerKind, managerColumnIds, addBaseColumn, removeBaseColumn, listBaseColumns, chatRoomConfigForLoaded, clearAckEndedSync, resolveAckRedirectDefaults, expandTargetSpec, targetRangeOptsFromProfile, } from "@seat-mesh/core";
5
5
  import { snapshotConnectivity, formatStatus } from "@seat-mesh/connectivity";
6
- import { createRegistryForProfile } from "@seat-mesh/providers";
7
- import { printWhoami, whoamiJson, whoamiJsonWithValidate, validateWhoamiRoleIndex, printAgentCard, printAgentContext, printAgentContextAllRoles, runWhoami, roleKindFromWhoami, requireCoordRole, requireInboxLifecycleRole, assertAgentDispatch, printAgentUnauthorized, stripAgentFromArgv, AGENT_META, capturePaneSnapshot, listSessionPanes, sessionAttach, sessionUp, sessionDown, sessionStatus, sessionSync, tmuxHasSession, relayoutMeshSession, realignAllLayouts, reloadMesh, ensureMeshInbox, startMeshInbox, stopMeshInbox, restartMeshInbox, meshInboxEverConfigured, meshInboxPort, printMeshInboxStatus, waitForMeshInbox, printInboxMeta, tailInboxLog, listInboxInstances, parseInboxStatusFlags, inboxStatusArgv, listInbox, resolveInbox, secretaryLaunch, secretaryDispatch, secretaryCollect, secretaryMeshWatch, secretarySupervise, secretaryStatus, printMiniList, miniSpawn, miniPrompt, miniDone, miniSpawnAll, runMeshSmoke, printSmokeResults, launchSession, printLaunchResults, defaultHarnessTypeForSeat, enqueuePrompt, injectPromptDirect, printPeerVerify, runPeerVerify, runRemind, printRemindResults, runNight, runContinue, printContinueResults, runSlotAdvice, verifyMeshSession, printVerify, labelMeshSession, liveMeshSession, ensureBaseLayout, realignBaseLayout, applyMeshSessionBorders, runFlush, printFlushResults, runSwitch, runPaneResume, runSeatSwap, runSet, runTag, setPaneTitle, setPaneStatus, printSeatContexts, runAgentHub, runPeek, runPaneKind, runPaneMeta, runPpa, runToSlot, runToMini, runPeer, parseRemotePeerTarget, runRemotePeer, applyMeshState, saveMeshSession, saveMeshSessionDetailed, assertSaveAllowed, formatSaveSummary, submitPaneOp, clearPaneOpsQueue, printPaneOpsList, printRelayoutPlan, assertRelayoutSafe, buildFullColdStartBrief, enqueueColdStart, runSeatInit, applyAgentContractBundle, balanceLeadCommand, } from "@seat-mesh/tmux";
8
- import { parseAgentApplyArgs } from "@seat-mesh/core";
9
- import { buildChatCommands } from "./commands/chat-cli.js";
6
+ import { createRegistryForProfile, loadDropInProviders } from "@seat-mesh/providers";
7
+ import { printWhoami, whoamiJson, whoamiJsonWithValidate, validateWhoamiRoleIndex, printAgentCard, printAgentContext, printAgentContextAllRoles, runWhoami, roleKindFromWhoami, requireCoordRole, requireInboxLifecycleRole, assertAgentDispatch, printAgentUnauthorized, stripAgentFromArgv, AGENT_META, capturePaneSnapshot, listSessionPanes, sessionAttach, sessionUp, sessionDown, sessionStatus, sessionSync, tmuxHasSession, relayoutMeshSession, realignAllLayouts, scaleLayout, layoutReload, reloadMesh, ensureMeshInbox, startMeshInbox, stopMeshInbox, restartMeshInbox, meshInboxEverConfigured, meshInboxPort, printMeshInboxStatus, waitForMeshInbox, printInboxMeta, tailInboxLog, listInboxInstances, parseInboxStatusFlags, inboxStatusArgv, listInbox, resolveInbox, secretaryLaunch, secretaryDispatch, secretaryCollect, secretaryMeshWatch, secretarySupervise, secretaryStatus, printMiniList, miniSpawn, miniPrompt, miniDone, miniSpawnAll, runMeshSmoke, printSmokeResults, launchSession, printLaunchResults, defaultHarnessTypeForSeat, enqueuePrompt, injectPromptDirect, printPeerVerify, runPeerVerify, runRemind, printRemindResults, runNight, runContinue, printContinueResults, runSlotAdvice, verifyMeshSession, printVerify, labelMeshSession, liveMeshSession, ensureBaseLayout, realignBaseLayout, applyMeshSessionBorders, runFlush, printFlushResults, runSwitch, runSwitchFast, runPaneResume, runSeatSwap, runSet, runTag, setPaneTitle, setPaneStatus, printSeatContexts, runAgentHub, runPeek, runPaneKind, runPaneMeta, runPaneCapture, runPpa, runToSlot, runToMini, runPeer, parseRemotePeerTarget, runRemotePeer, applyMeshState, saveMeshSession, saveMeshSessionDetailed, assertSaveAllowed, formatSaveSummary, submitPaneOp, clearPaneOpsQueue, printPaneOpsList, printRelayoutPlan, assertRelayoutSafe, buildFullColdStartBrief, enqueueColdStart, runSeatInit, applyAgentContractBundle, balanceLeadCommand, kindsForLoaded, } from "@seat-mesh/tmux";
8
+ import { parseAgentApplyArgs, lookupResolvedKind } from "@seat-mesh/core";
10
9
  import { buildCheckbackCommands } from "./commands/checkback-cli.js";
11
10
  import { buildTargetCommands } from "./commands/target-cli.js";
12
11
  import { buildAckCommands } from "./commands/ack-cli.js";
13
12
  import { resolvePeerEndedAckId, shouldAutoAckReply, } from "./commands/ack-reply.js";
14
13
  import { buildLimitCommands } from "./commands/limit-cli.js";
15
14
  import { buildRolesCommands } from "./commands/roles-cli.js";
16
- import { buildNotifyCommand } from "./commands/notify-cli.js";
17
- import { buildPreviewCommand } from "./commands/preview-cli.js";
18
- import { runMdsCommand } from "./commands/mds-cli.js";
19
- import { buildContractLockCommands } from "./commands/contract-lock-cli.js";
20
- import { buildRoomCommands } from "./commands/room-cli.js";
21
15
  import { runInit } from "./setup/init.js";
22
16
  import { runAgentContextInit } from "./setup/agent-context-init.js";
23
17
  import { printSessionCheck, printSessionRepair, runSessionCheck, runSessionRepair, } from "./setup/session-health.js";
@@ -62,8 +56,10 @@ Setup
62
56
 
63
57
  Put an agent on a pane
64
58
  help human ← cheat sheet (aliases: put-agent | panes)
65
- switch <target> <opencode|opencode-cpe|claude|agent|kiro|empty>
66
- launch <target|all>
59
+ spawn <target> <opencode|opencode-cpe|claude|agent|kiro> empty → CLI
60
+ switch <target> <…|empty> replace live agent (or → shell)
61
+ kill|empty <target> pane → plain terminal (+ save empty)
62
+ launch <target|all> resume configured CLI
67
63
  kind <target>
68
64
 
69
65
  Work / status
@@ -73,7 +69,9 @@ Work / status
73
69
  report | verify | test | save|auto | inbox […] | labels
74
70
 
75
71
  Session shape
76
- reload [--layout] | layout […] | realign | ops list|clear
72
+ rebuild|reload [--layout] npm build + labels (not config→pane)
73
+ layout reload re-grid + resume from mesh-agents.json
74
+ layout […] | realign | ops list|clear
77
75
 
78
76
  Agent panes use the gateway (not listed here by default):
79
77
  seatmesh agent … · seatmesh agent help
@@ -104,6 +102,7 @@ Setup (run once per project, by a human)
104
102
  config check | config upgrade validate yaml · how to bump CLI (npm i -g)
105
103
  web up|down|restart|status|open|url operator hub :3190 (npx seatmesh web up)
106
104
  roles status|migrate [--to VER]|steps locked role-pack up/down (1.1.x)
105
+ host up|down|status opt-in single host-supervisor for all registered meshes (Phase 1)
107
106
  version [--json] [--check-registry] CLI vs npm latest vs profile .seatmesh-version
108
107
 
109
108
  Put an agent on a pane (human — most common)
@@ -284,6 +283,13 @@ async function main() {
284
283
  console.log(` bin dir: ${r.binDir} (ensure it is on PATH)`);
285
284
  return;
286
285
  }
286
+ if (cmd === "host") {
287
+ const { runHostCommand } = await import("./commands/host-cli.js");
288
+ const code = runHostCommand([sub, ...tail].filter((a) => a != null));
289
+ if (code !== 0)
290
+ process.exit(code);
291
+ return;
292
+ }
287
293
  if (cmd === "update") {
288
294
  const { runUpdate } = await import("./setup/update.js");
289
295
  const { formatInstallStatusLine, isNpxEphemeralInstall, readInstalledCliVersion, resolveLatestRegistryVersion, semverLess, } = await import("./ui/version-nudge.js");
@@ -645,13 +651,13 @@ async function main() {
645
651
  const inboxOk = printMeshInboxStatus(loaded);
646
652
  process.exit(layoutOk && inboxOk ? 0 : 1);
647
653
  }
648
- if (cmd === "reload") {
654
+ if (cmd === "reload" || cmd === "rebuild") {
649
655
  const loaded = meshLoaded(profileArg);
650
656
  const layout = rest.includes("--layout");
651
657
  reloadMesh(loaded, { layout });
652
658
  console.log(layout
653
- ? `OK: reload + relayout session ${loaded.sessionName}`
654
- : `OK: reload (build + labels) session ${loaded.sessionName}`);
659
+ ? `OK: rebuild + relayout session ${loaded.sessionName}`
660
+ : `OK: rebuild (npm build + labels) session ${loaded.sessionName} — not config→pane`);
655
661
  printMeshInboxStatus(loaded);
656
662
  return;
657
663
  }
@@ -668,6 +674,87 @@ async function main() {
668
674
  }
669
675
  return;
670
676
  }
677
+ if (cmd === "layout" && sub === "scale") {
678
+ const loaded = meshLoaded(profileArg);
679
+ const kindRaw = (tail[0] ?? "").toLowerCase();
680
+ const kind = kindRaw === "workers" || kindRaw === "worker" || kindRaw === "w"
681
+ ? "workers"
682
+ : kindRaw === "minis" || kindRaw === "mini" || kindRaw === "m"
683
+ ? "minis"
684
+ : null;
685
+ const action = (tail[1] ?? "").toLowerCase();
686
+ const force = rest.includes("--yes");
687
+ const dryRun = rest.includes("--dry-run");
688
+ if (!kind || !action) {
689
+ console.error("usage: layout scale workers|minis up|down|<N> [--yes] [--dry-run]");
690
+ process.exit(2);
691
+ }
692
+ requireCoordRole(loaded, "layout scale");
693
+ if (action !== "up" && action !== "down") {
694
+ const n = Number(action);
695
+ if (!Number.isInteger(n) || n < 1) {
696
+ console.error("usage: layout scale workers|minis up|down|<N> [--yes] [--dry-run]");
697
+ process.exit(2);
698
+ }
699
+ }
700
+ try {
701
+ const r = action === "up" || action === "down"
702
+ ? scaleLayout(loaded, {
703
+ kind,
704
+ dir: action,
705
+ force,
706
+ dryRun,
707
+ })
708
+ : scaleLayout(loaded, {
709
+ kind,
710
+ to: Number(action),
711
+ force,
712
+ dryRun,
713
+ });
714
+ if (r.dryRun) {
715
+ console.log(`dry-run: scale ${r.kind} ${r.from} → ${r.to} (${r.grid})`);
716
+ return;
717
+ }
718
+ if (r.from === r.to) {
719
+ console.log(`OK: scale ${r.kind} already ${r.to} (${r.grid})`);
720
+ return;
721
+ }
722
+ console.log(`OK: scale ${r.kind} ${r.from} → ${r.to} (${r.grid})${r.saved ? ` saved ${r.saved}` : ""}`);
723
+ }
724
+ catch (e) {
725
+ console.error(e.message);
726
+ process.exit(1);
727
+ }
728
+ return;
729
+ }
730
+ if (cmd === "layout" && sub === "reload") {
731
+ const loaded = meshLoaded(profileArg);
732
+ const force = rest.includes("--yes");
733
+ const skipLeads = rest.includes("--no-leads");
734
+ const noResume = rest.includes("--no-resume");
735
+ requireCoordRole(loaded, "layout reload");
736
+ try {
737
+ const r = layoutReload(loaded, {
738
+ force,
739
+ skipMinisLeads: skipLeads,
740
+ noResume,
741
+ });
742
+ const launched = r.repaired.filter((x) => x.status === "launched");
743
+ const failed = r.repaired.filter((x) => x.status === "failed");
744
+ console.log(`OK: layout reload ${loaded.sessionName} repaired=${launched.length}${r.saved ? ` saved ${r.saved}` : ""}`);
745
+ for (const x of launched) {
746
+ console.log(` resume ${x.label} ${x.paneId}`);
747
+ }
748
+ for (const x of failed) {
749
+ console.warn(` fail ${x.label}: ${x.reason ?? x.status}`);
750
+ }
751
+ }
752
+ catch (e) {
753
+ console.error(e.message);
754
+ process.exit(1);
755
+ }
756
+ return;
757
+ }
671
758
  if (cmd === "layout" && sub === "column") {
672
759
  const loaded = meshLoaded(profileArg);
673
760
  const action = tail[0];
@@ -739,7 +826,7 @@ async function main() {
739
826
  const leadNote = skipLeads || !m
740
827
  ? ""
741
828
  : ` leads=[${Array.isArray(m.leads) ? m.leads.join(",") : "1,2"}]`;
742
- console.log(`OK: relayout ${loaded.sessionName} (workers 3x2, minis ${grid}${leadNote})`);
829
+ console.log(`OK: relayout ${loaded.sessionName} (workers ${loaded.profile.layout?.workers.grid ?? "?"}, minis ${grid}${leadNote})`);
743
830
  console.log(`OK: layout saved ${saved}`);
744
831
  });
745
832
  return;
@@ -962,15 +1049,52 @@ async function main() {
962
1049
  printPeerVerify(r);
963
1050
  process.exit(r.pass ? 0 : 1);
964
1051
  }
965
- const [target, ...textParts] = args;
1052
+ const [targetRaw, ...textParts] = args;
966
1053
  const rawMsg = textParts.join(" ").trim();
967
- if (!target || !rawMsg) {
1054
+ if (!targetRaw || !rawMsg) {
968
1055
  console.error("usage: peer verify [target] | peer [--direct] [--ended|--ack [ack-id]] [--redirect-from <seat>] <target> <msg...>\n" +
1056
+ " target: seat | 1..4 | slot-1..3 | mini-1..2 | 1,3,5 (Ruby-style fanout)\n" +
969
1057
  " --ack / --ended [id] close open ask (id optional = auto-match from target)\n" +
970
1058
  " ack-class msg (ACK/FYI/PASS) auto-closes matching open ask\n" +
971
1059
  " or: agent ack reply <id> [msg] (peer back + close in one shot)");
972
1060
  process.exit(2);
973
1061
  }
1062
+ const peerTargets = expandTargetSpec(targetRaw, targetRangeOptsFromProfile(loaded.profile));
1063
+ if (peerTargets.length > 1 && endedId) {
1064
+ console.error("WARN: --ack/--ended ignored on range fanout (send-only)");
1065
+ endedId = undefined;
1066
+ }
1067
+ const target = peerTargets[0];
1068
+ if (peerTargets.length > 1) {
1069
+ for (const t of peerTargets) {
1070
+ try {
1071
+ runPeer(loaded, t, rawMsg);
1072
+ }
1073
+ catch (e) {
1074
+ if (e.message !== "__peer_coord__") {
1075
+ console.error(`FAIL peer ${t}: ${e.message}`);
1076
+ continue;
1077
+ }
1078
+ requireCoordRole(loaded, "peer");
1079
+ if (direct) {
1080
+ const reg = createRegistryForProfile(loaded.profile);
1081
+ injectPromptDirect(loaded, reg, t, rawMsg, { manager: true, force: true });
1082
+ console.log(`SENT: peer --direct -> ${t}`);
1083
+ }
1084
+ else {
1085
+ const { paneId, targetLabel, token, via } = enqueuePrompt(loaded, t, rawMsg, {
1086
+ manager: true,
1087
+ armCheckback: false,
1088
+ });
1089
+ console.log(via === "queued"
1090
+ ? `QUEUED: peer -> ${targetLabel} pane=${paneId} token=${token ?? "-"}`
1091
+ : `SENT: peer -> ${targetLabel} pane=${paneId}`);
1092
+ }
1093
+ }
1094
+ }
1095
+ console.log(`ok fanout peer → ${peerTargets.join(",")} (${peerTargets.length})`);
1096
+ return;
1097
+ }
974
1098
  if (shouldAutoAckReply(rawMsg, endedId)) {
975
1099
  endedId = "__auto__";
976
1100
  }
@@ -1180,6 +1304,13 @@ async function main() {
1180
1304
  secretaryLaunch(loaded);
1181
1305
  return;
1182
1306
  }
1307
+ if (sub === "stop") {
1308
+ const reg = createRegistryForProfile(loaded.profile);
1309
+ runSwitch(loaded, reg, "secretary", "empty", { reason: "stop" });
1310
+ saveMeshSession(loaded, reg);
1311
+ console.log("OK: secretary stopped (pane -> empty shell)");
1312
+ return;
1313
+ }
1183
1314
  if (sub === "restart" || sub === "switch") {
1184
1315
  const reg = createRegistryForProfile(loaded.profile);
1185
1316
  const keepResume = rest.includes("--keep-resume") ||
@@ -1302,6 +1433,37 @@ async function main() {
1302
1433
  }
1303
1434
  return;
1304
1435
  }
1436
+ if (cmd === "kind" && (sub === "list" || sub === "show")) {
1437
+ const loaded = meshLoaded(profileArg);
1438
+ const kinds = kindsForLoaded(loaded);
1439
+ if (sub === "list") {
1440
+ for (const id of Object.keys(kinds).sort()) {
1441
+ const k = kinds[id];
1442
+ const launch = k.launch === undefined
1443
+ ? "default"
1444
+ : k.launch === null
1445
+ ? "empty"
1446
+ : "builtin" in k.launch
1447
+ ? `builtin:${k.launch.builtin}`
1448
+ : `script:${k.launch.command}`;
1449
+ const aliases = k.aliases?.length ? ` aliases=${k.aliases.join(",")}` : "";
1450
+ console.log(`${id}\tprovider=${k.provider}\tlaunch=${launch}${aliases}`);
1451
+ }
1452
+ return;
1453
+ }
1454
+ const id = tail[0];
1455
+ if (!id) {
1456
+ console.error("usage: kind show <id>");
1457
+ process.exit(2);
1458
+ }
1459
+ const found = lookupResolvedKind(kinds, id);
1460
+ if (!found) {
1461
+ console.error(`kind show: unknown kind "${id}" (see: seatmesh kind list)`);
1462
+ process.exit(1);
1463
+ }
1464
+ console.log(JSON.stringify(found, null, 2));
1465
+ return;
1466
+ }
1305
1467
  if (cmd === "kind" || cmd === "what" || cmd === "typeof") {
1306
1468
  const loaded = meshLoaded(profileArg);
1307
1469
  const reg = createRegistryForProfile(loaded.profile);
@@ -1328,6 +1490,17 @@ async function main() {
1328
1490
  }
1329
1491
  return;
1330
1492
  }
1493
+ if (cmd === "capture") {
1494
+ const loaded = meshLoaded(profileArg);
1495
+ try {
1496
+ runPaneCapture(loaded, [sub, ...tail].filter((a) => Boolean(a)));
1497
+ }
1498
+ catch (e) {
1499
+ console.error(e.message);
1500
+ process.exit(1);
1501
+ }
1502
+ return;
1503
+ }
1331
1504
  if (cmd === "pane") {
1332
1505
  const loaded = meshLoaded(profileArg);
1333
1506
  const reg = createRegistryForProfile(loaded.profile);
@@ -1476,12 +1649,16 @@ async function main() {
1476
1649
  const loaded = meshLoaded(profileArg);
1477
1650
  requireCoordRole(loaded, "launch");
1478
1651
  const rawArgs = [sub, ...tail].filter((a) => Boolean(a) && a !== "--");
1479
- const launchNow = rawArgs.includes("--now");
1480
- const targets = rawArgs.filter((a) => a !== "--now");
1481
- const label = targets.length ? targets.join(",") : "all";
1652
+ const launchNow = rawArgs.includes("--now") || rawArgs.includes("--fast");
1653
+ const targetsRaw = rawArgs.filter((a) => a !== "--now" && a !== "--fast");
1654
+ const rangeOpts = targetRangeOptsFromProfile(loaded.profile);
1655
+ const targets = targetsRaw.length
1656
+ ? targetsRaw.flatMap((t) => expandTargetSpec(t, rangeOpts))
1657
+ : undefined;
1658
+ const label = targets?.length ? targets.join(",") : "all";
1482
1659
  const runLaunch = () => {
1483
1660
  const results = launchSession(loaded, {
1484
- targets: targets.length ? targets : undefined,
1661
+ targets,
1485
1662
  });
1486
1663
  printLaunchResults(results);
1487
1664
  if (results.some((r) => r.status === "failed"))
@@ -1491,7 +1668,7 @@ async function main() {
1491
1668
  runLaunch();
1492
1669
  return;
1493
1670
  }
1494
- submitPaneOp(loaded, "launch", { targets: targets.length ? targets : undefined }, `launch ${label}`, runLaunch);
1671
+ submitPaneOp(loaded, "launch", { targets }, `launch ${label}`, runLaunch);
1495
1672
  return;
1496
1673
  }
1497
1674
  if (cmd === "prompt") {
@@ -1615,17 +1792,43 @@ async function main() {
1615
1792
  printFlushResults(results);
1616
1793
  return;
1617
1794
  }
1618
- if (cmd === "switch" || cmd === "handoff") {
1795
+ if (cmd === "kill" || cmd === "empty") {
1619
1796
  const loaded = meshLoaded(profileArg);
1620
- requireCoordRole(loaded, cmd);
1621
- const reg = createRegistryForProfile(loaded.profile);
1797
+ requireCoordRole(loaded, "switch");
1798
+ const targetRaw = sub;
1799
+ if (!targetRaw) {
1800
+ console.error(`usage: ${cmd} <target|1..4|slot-N|mini-N|here>`);
1801
+ console.error(" → plain terminal (respawn-pane -k) + save empty in mesh-agents.json");
1802
+ process.exit(2);
1803
+ }
1804
+ const targets = expandTargetSpec(targetRaw, targetRangeOptsFromProfile(loaded.profile));
1805
+ for (const t of targets) {
1806
+ runSwitchFast(loaded, t, "empty", {
1807
+ fresh: true,
1808
+ persistEmpty: true,
1809
+ reason: cmd === "kill" ? "kill" : "empty",
1810
+ });
1811
+ }
1812
+ if (targets.length > 1) {
1813
+ console.log(`ok fanout ${cmd} → ${targets.join(",")} (${targets.length})`);
1814
+ }
1815
+ return;
1816
+ }
1817
+ if (cmd === "switch" || cmd === "handoff" || cmd === "spawn") {
1818
+ const loaded = meshLoaded(profileArg);
1819
+ requireCoordRole(loaded, cmd === "spawn" ? "switch" : cmd);
1622
1820
  const args = [sub, ...tail].filter(Boolean);
1821
+ const vocal = cmd === "spawn" ? "spawn" : "switch";
1822
+ // spawn defaults fast (paste like typing opencode); switch defaults thorough
1823
+ let fast = cmd === "spawn";
1623
1824
  if (args.length < 2) {
1624
- console.error("usage: switch <target> <agent|cursor|claude|cc|kiro|oc|opencode|empty> [--keep-resume] [--resume ID] [--queue] [reason...]");
1625
- console.error(" targets: secretary|sec, manager|mgr, slot-N, mini-N, here|self (this pane's role)");
1825
+ console.error(cmd === "spawn"
1826
+ ? "usage: spawn <target|1..4> <agent|cursor|claude|oc|opencode> [--fast|--slow] [--keep-resume] [--resume ID]"
1827
+ : "usage: switch <target|1..4> <agent|…|empty> [--fast|--slow] [--keep-resume] [--resume ID] [reason...]");
1828
+ console.error(" empty→CLI: spawn (fast by default) · replace live: switch · --fast = skip verify waits");
1626
1829
  process.exit(2);
1627
1830
  }
1628
- const target = args[0];
1831
+ const targetRaw = args[0];
1629
1832
  const newType = args[1];
1630
1833
  let fresh;
1631
1834
  let queue = false;
@@ -1637,6 +1840,10 @@ async function main() {
1637
1840
  fresh = true;
1638
1841
  else if (a === "--keep-resume" || a === "--no-fresh")
1639
1842
  fresh = false;
1843
+ else if (a === "--fast")
1844
+ fast = true;
1845
+ else if (a === "--slow" || a === "--verify")
1846
+ fast = false;
1640
1847
  else if (a === "--queue")
1641
1848
  queue = true;
1642
1849
  else if (a === "--resume" && args[i + 1])
@@ -1647,17 +1854,29 @@ async function main() {
1647
1854
  reasonParts.push(a);
1648
1855
  }
1649
1856
  const reason = reasonParts.join(" ") || undefined;
1650
- const run = () => runSwitch(loaded, reg, target, newType, {
1857
+ const targets = expandTargetSpec(targetRaw, targetRangeOptsFromProfile(loaded.profile));
1858
+ // Fast path skips provider registry build (expensive).
1859
+ const reg = fast ? null : createRegistryForProfile(loaded.profile);
1860
+ const runOne = (target) => runSwitch(loaded, reg, target, newType, {
1651
1861
  fresh,
1652
1862
  resumeId,
1653
1863
  reason,
1864
+ fast,
1654
1865
  });
1866
+ const run = () => {
1867
+ for (const t of targets)
1868
+ runOne(t);
1869
+ if (targets.length > 1) {
1870
+ console.log(`ok fanout ${vocal} → ${targets.join(",")} (${targets.length})`);
1871
+ }
1872
+ };
1655
1873
  if (queue) {
1656
- submitPaneOp(loaded, "switch", { target, newType, fresh, resumeId, reason }, `switch ${target} -> ${newType}`, run);
1874
+ submitPaneOp(loaded, "switch", { target: targetRaw, newType, fresh, resumeId, reason, fast }, `${vocal} ${targetRaw} -> ${newType}${fast ? " [fast]" : ""}`, run);
1657
1875
  }
1658
1876
  else {
1659
1877
  run();
1660
- saveMeshSession(loaded, reg);
1878
+ if (!fast && reg)
1879
+ saveMeshSession(loaded, reg);
1661
1880
  }
1662
1881
  return;
1663
1882
  }
@@ -1777,7 +1996,8 @@ async function main() {
1777
1996
  }
1778
1997
  if (sub && AGENT_META.has(sub)) {
1779
1998
  if (sub === "forum" || sub === "golf") {
1780
- console.log("agent forum/golf — see docs/cli/forum.md (not bundled in this build)");
1999
+ const { printAgentForum } = await import("./commands/agent-forum.js");
2000
+ printAgentForum();
1781
2001
  return;
1782
2002
  }
1783
2003
  if (sub === "apply") {
@@ -1983,6 +2203,10 @@ async function main() {
1983
2203
  if (cmd === "providers") {
1984
2204
  const loaded = meshLoaded(profileArg);
1985
2205
  const reg = createRegistryForProfile(loaded.profile);
2206
+ const path = await import("node:path");
2207
+ const dropIn = await loadDropInProviders(path.join(loaded.profileDir, "providers"), (line) => console.error(line));
2208
+ for (const p of dropIn.providers)
2209
+ reg.register(p);
1986
2210
  if (sub === "list") {
1987
2211
  for (const p of reg.all())
1988
2212
  console.log(p.id);
@@ -2047,10 +2271,10 @@ async function main() {
2047
2271
  const loaded = meshLoaded(profileArg);
2048
2272
  const getLoaded = () => loaded;
2049
2273
  const branch = cmd === "room"
2050
- ? buildRoomCommands(getLoaded)
2274
+ ? (await import("./commands/room-cli.js")).buildRoomCommands(getLoaded)
2051
2275
  : cmd === "contract"
2052
- ? buildContractLockCommands(getLoaded)
2053
- : buildChatCommands(getLoaded);
2276
+ ? (await import("./commands/contract-lock-cli.js")).buildContractLockCommands(getLoaded)
2277
+ : (await import("./commands/chat-cli.js")).buildChatCommands(getLoaded);
2054
2278
  // contract with no sub → status (simple agent path)
2055
2279
  if (!sub && cmd === "contract") {
2056
2280
  try {
@@ -2174,7 +2398,7 @@ async function main() {
2174
2398
  if (cmd === "notify") {
2175
2399
  const loaded = meshLoaded(profileArg);
2176
2400
  const getLoaded = () => loaded;
2177
- const branch = buildNotifyCommand(getLoaded);
2401
+ const branch = (await import("./commands/notify-cli.js")).buildNotifyCommand(getLoaded);
2178
2402
  try {
2179
2403
  await branch.parseAsync(rest.slice(1), { from: "user" });
2180
2404
  }
@@ -2188,12 +2412,66 @@ async function main() {
2188
2412
  }
2189
2413
  if (cmd === "mds") {
2190
2414
  const loaded = meshLoaded(profileArg);
2415
+ const { runMdsCommand } = await import("./commands/mds-cli.js");
2191
2416
  process.exit(await runMdsCommand(loaded, rest.slice(1)));
2192
2417
  }
2193
2418
  if (cmd === "preview") {
2194
2419
  const loaded = meshLoaded(profileArg);
2195
2420
  const getLoaded = () => loaded;
2196
- const branch = buildPreviewCommand(getLoaded);
2421
+ const branch = (await import("./commands/preview-cli.js")).buildPreviewCommand(getLoaded);
2422
+ try {
2423
+ await branch.parseAsync(rest.slice(1), { from: "user" });
2424
+ }
2425
+ catch (e) {
2426
+ const err = e;
2427
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version")
2428
+ return;
2429
+ throw e;
2430
+ }
2431
+ return;
2432
+ }
2433
+ if (cmd === "nav") {
2434
+ const loaded = meshLoaded(profileArg);
2435
+ const getLoaded = () => loaded;
2436
+ const branch = (await import("./commands/nav-cli.js")).buildNavCommands(getLoaded);
2437
+ if (!sub) {
2438
+ branch.outputHelp();
2439
+ return;
2440
+ }
2441
+ try {
2442
+ await branch.parseAsync([sub, ...tail], { from: "user" });
2443
+ }
2444
+ catch (e) {
2445
+ const err = e;
2446
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version")
2447
+ return;
2448
+ throw e;
2449
+ }
2450
+ return;
2451
+ }
2452
+ if (cmd === "campaign") {
2453
+ const loaded = meshLoaded(profileArg);
2454
+ const getLoaded = () => loaded;
2455
+ const branch = (await import("./commands/campaign-cli.js")).buildCampaignCommands(getLoaded);
2456
+ if (!sub) {
2457
+ branch.outputHelp();
2458
+ return;
2459
+ }
2460
+ try {
2461
+ await branch.parseAsync([sub, ...tail], { from: "user" });
2462
+ }
2463
+ catch (e) {
2464
+ const err = e;
2465
+ if (err.code === "commander.helpDisplayed" || err.code === "commander.version")
2466
+ return;
2467
+ throw e;
2468
+ }
2469
+ return;
2470
+ }
2471
+ if (cmd === "schedule") {
2472
+ const loaded = meshLoaded(profileArg);
2473
+ const getLoaded = () => loaded;
2474
+ const branch = (await import("./commands/schedule-cli.js")).buildScheduleCommand(getLoaded);
2197
2475
  try {
2198
2476
  await branch.parseAsync(rest.slice(1), { from: "user" });
2199
2477
  }