Technical reference for the maw hey command. Source: Soul-Brews-Studio/maw-js. As of v26.4.28 stable / v26.4.28-alpha.21 (Phase 1 of #736 wake-unify merged: PRs #780 + #781).
maw hey <target> <message> [--force] [--quiet]
maw hey sends a UTF-8 message to one running agent. The target may be on the local machine (delivered via tmux) or on a federated peer (delivered via HTTP). The verb is messaging-oriented — it refuses to send to non-agent panes unless --force is given.
Three forms, listed by ambiguity (least → most):
| Form | Example | Resolves to |
|---|---|---|
<node>:<session>:<window> |
phaith:01-hojo:3 |
exact tmux window on a node |
<node>:<agent> |
phaith:hojo |
strict exact-match on writable sessions |
<agent> |
hojo |
(deprecated #759) bare-name lookup |
<node> may be local (alias for current node).
| Flag | Effect |
|---|---|
--force |
bypass the Claude-readiness guard (send to bash/non-agent panes) |
--quiet |
suppress deprecation warnings (also via MAW_QUIET=1) |
--help |
print short help |
resolveTarget is pure, synchronous, no network. Returns one of four verdicts:
| Verdict | Meaning |
|---|---|
local |
writable tmux window on this node |
self-node |
node prefix names this node, treat as local |
peer |
node prefix matches a namedPeers[].name |
error |
none of the above |
Resolution order:
- Auto-wake check (PR #780, alpha.21): if target is bare-name or
local:<name>AND fleet-known AND no live session, silently callcmdWake(target, {})and refresh the session list before resolving. No y/N prompt — fleet membership is sufficient signal that the name isn't a typo. Cross-node<peer>:<agent>skipped here; remote peer's/api/wakehandles its own fleet. - Filter local sessions: drop
*-viewmirrors and any session withsource != "local". - Match query against the remaining writable set.
- If no local match and query has
:and no/, split on first:, look up left side innamedPeers. →peer. - If query is bare, look up in flat
agentsmap (<agent> → <node>). Resolve transitively. - Otherwise →
error.
agents map is auto-populated from fleet at loadConfig() (PR #781, alpha.20): ~/.config/maw/fleet/*.json is scanned and windows[].name → <session's node> entries are merged into config.agents. Manual entries in the config still take precedence. Closes the gap where bare-name resolution failed for fleet targets that hadn't been waked yet.
Ambiguity is fatal — the command refuses with the candidate list.
When verdict is local or self-node:
- Auto-wake (PR #780): already handled in resolution step 1; by the time we reach delivery the session exists.
- Pane resolution (
resolveOraclePane): enumerate panes in the target window, match process againstclaude|codex|node, pin to lowest-index match. - Readiness guard: refuse if pane process is
bashor other non-agent. - Idle guard (#405): refuse if user appears to be mid-typing.
- Send:
- long/multiline:
load-buffer+paste-buffer - short:
send-keys -l
- long/multiline:
- Submit: three staggered Enters at 0ms / 700ms / 1900ms.
When verdict is peer:
POST {peerUrl}/api/send
Content-Type: application/json
Authorization: Bearer {federationToken}
{ "target": "01-hojo:3", "text": "ready when you are" }
Successful response:
{
"ok": true,
"target": "01-hojo:3",
"lastLine": "│ > _"
}
Local CLI prints delivered ⚡ <node> → <target>: <message> and exits 0.
Path: ~/.config/maw/maw.config.json
{
"node": "white", // this node's name
"port": 3456, // federation server port
"federationToken": "<shared-secret>", // bearer token for peer auth
"namedPeers": [
{ "name": "phaith", "url": "http://phaith.local:3456" },
{ "name": "m5", "url": "http://m5.local:3456" }
],
"agents": { // bare-name → node lookup
"hojo": "phaith", // (manual entries take precedence)
"homekeeper": "mba" // (auto-merged from fleet at loadConfig — PR #781)
}
}
federationToken must match across all peers in the federation. There is no rotation protocol — coordinate manually and restart maw serve on each peer.
| Code | Meaning |
|---|---|
| 0 | delivered |
| 1 | usage error / bad args |
| 2 | target ambiguous (multiple writable matches) |
| 3 | target not found |
| 4 | readiness guard refused (use --force) |
| 5 | idle guard refused (#405) |
| 22 | HTTP 4xx/5xx from peer (curl exit code 22) |
| 28 | timeout / peer unreachable (curl exit code 28) |
HTTP 0 from --verbose output indicates connection refused.
error: no active Claude session in <target> (running: bash)
hint: run `maw wake <agent>` first, or use --force to send anyway
# Note: post-#780 (alpha.21+), this fires only when:
# - target is NOT fleet-known (auto-wake skipped), OR
# - target is fleet-known but pane process is bash (manual wake didn't launch agent)
# Fleet-known + no-session targets now auto-wake silently before this guard runs.
error: target ambiguous — N candidates:
• <candidate-1>
• <candidate-2>
use the full name: maw hey <node>:<session>:<window>
error: oracle repo not found: <name>
(tried ghq, fleet configs, worktree scan, GitHub clone, and N peers)
⚠ deprecation: bare-name target '<name>' is deprecated and will be removed (#759)
this node:
maw hey local:<name> "..."
run `maw locate <name>` to enumerate cross-node candidates
| Command | Purpose |
|---|---|
maw peek <target> |
read last N lines (read-only, async) |
maw broadcast <targets...> <msg> |
hey to many, sequential |
maw send-enter <target> |
Enter-only, bypasses readiness guard |
maw locate <agent> |
resolve target without sending |
maw wake <agent> |
start agent (clone repo, create session, launch) |
maw sleep <agent> |
stop agent (kill session) |
maw health |
probe federation peers (/info endpoint) |
maw ls |
list local + federated sessions |
maw peers list |
show registered peers |
Local agent on this machine:
$ maw hey hojo "ping"
delivered ⚡ local → 01-hojo:1: ping
Cross-node, canonical:
$ maw hey phaith:01-hojo:3 "build green?"
delivered ⚡ phaith → 01-hojo:3: build green?
Cross-node, short form (post-#758 strict match):
$ maw hey phaith:hojo "ping"
delivered ⚡ phaith → 01-hojo:1: ping
Bare-name (deprecated, prints warning):
$ maw hey hojo "ping"
⚠ deprecation: bare-name target 'hojo' is deprecated...
delivered ⚡ local → 01-hojo:1: ping
Force-send to a shell pane:
$ maw hey thclaws-build --force "echo hi"
delivered ⚡ local → thclaws-build:1: echo hi
Suppress deprecation warning:
$ MAW_QUIET=1 maw hey hojo "ping"
delivered ⚡ local → 01-hojo:1: ping
Each fix narrowed the resolver's notion of "almost match":
| Bug | Cause | Fix |
|---|---|---|
| #758 | findWindow matched -view mirrors and remote-source records |
filter before ambiguity check |
| #762 | talk-to called findWindow directly, bypassing resolver |
route through resolveTarget |
| #768 | fleet lookup keyed on ${oracle}-oracle, missed bare names |
accept either form |
| #769 | wake URL → token did greedy substring match against sessions |
strict exact-match when URL given |
| #770 | wake scan iterated all 24+ locally-cloned orgs | filter to owned/member orgs |
Phase 1 closed the gap where maw hey required pre-existing live sessions while maw view auto-waked from fleet:
| PR | Change | Effect |
|---|---|---|
| #781 | config.agents auto-merged from ~/.config/maw/fleet/*.json at loadConfig() |
bare-name resolution works for fleet targets that have never been waked |
| #780 | cmdSend calls cmdWake(target, {}) when target is fleet-known and no session exists |
maw hey <fleet-agent> no longer errors on cold start; silently wakes first |
Phase 2 (refactor) pending.
| Var | Effect |
|---|---|
MAW_QUIET=1 |
suppress deprecation warnings |
MAW_CONFIG |
override config path (default ~/.config/maw/maw.config.json) |
MAW_PORT |
override federation port (default 3456) |
~/.config/maw/maw.config.json primary config
~/.maw/peers.json peer alias storage (separate from namedPeers)
~/.config/maw/fleet/*.json fleet session configs
- The probe path (
/info, used bymaw health) and the write path (/api/send) hit different handlers and fail independently. Trustdelivered ⚡overmaw healthsummaries. - Federation transport is HTTP-over-mDNS (
.localhostnames) by default; no built-in retry semantics. federationTokenis a shared secret — the security model assumes one human's machines, not strangers.--forcesemantics may move tomaw send/maw runper #757.- Auto-wake is silent (post-#780). If you depend on the old "no active Claude session" error to detect missing sessions, use
maw locate <agent>— it returns the verdict without sending or waking.
maw-peek(1), maw-broadcast(1), maw-send-enter(1), maw-locate(1), maw-wake(1), maw-health(1)