Skip to content

Instantly share code, notes, and snippets.

@nazt
Last active April 28, 2026 10:01
Show Gist options
  • Select an option

  • Save nazt/09c8389e11f6d519bb4f39e130860f7c to your computer and use it in GitHub Desktop.

Select an option

Save nazt/09c8389e11f6d519bb4f39e130860f7c to your computer and use it in GitHub Desktop.
maw hey(1) — CLI reference for the maw-js federation messaging command, v26.4.28

maw-hey(1) — send a message to an agent

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).

SYNOPSIS

maw hey <target> <message> [--force] [--quiet]

DESCRIPTION

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.

TARGET FORMS

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).

OPTIONS

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

TARGET RESOLUTION

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:

  1. Auto-wake check (PR #780, alpha.21): if target is bare-name or local:<name> AND fleet-known AND no live session, silently call cmdWake(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/wake handles its own fleet.
  2. Filter local sessions: drop *-view mirrors and any session with source != "local".
  3. Match query against the remaining writable set.
  4. If no local match and query has : and no /, split on first :, look up left side in namedPeers. → peer.
  5. If query is bare, look up in flat agents map (<agent> → <node>). Resolve transitively.
  6. 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.

LOCAL DELIVERY

When verdict is local or self-node:

  1. Auto-wake (PR #780): already handled in resolution step 1; by the time we reach delivery the session exists.
  2. Pane resolution (resolveOraclePane): enumerate panes in the target window, match process against claude|codex|node, pin to lowest-index match.
  3. Readiness guard: refuse if pane process is bash or other non-agent.
  4. Idle guard (#405): refuse if user appears to be mid-typing.
  5. Send:
    • long/multiline: load-buffer + paste-buffer
    • short: send-keys -l
  6. Submit: three staggered Enters at 0ms / 700ms / 1900ms.

REMOTE DELIVERY

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.

CONFIG SCHEMA

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.

EXIT CODES

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 MESSAGES

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

RELATED COMMANDS

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

EXAMPLES

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

RESOLVER BUG HISTORY

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

SEMANTIC CHANGES (#736 wake-unify, Phase 1)

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.

ENVIRONMENT VARIABLES

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)

FILES

~/.config/maw/maw.config.json   primary config
~/.maw/peers.json               peer alias storage (separate from namedPeers)
~/.config/maw/fleet/*.json      fleet session configs

NOTES

  • The probe path (/info, used by maw health) and the write path (/api/send) hit different handlers and fail independently. Trust delivered ⚡ over maw health summaries.
  • Federation transport is HTTP-over-mDNS (.local hostnames) by default; no built-in retry semantics.
  • federationToken is a shared secret — the security model assumes one human's machines, not strangers.
  • --force semantics may move to maw send / maw run per #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.

SEE ALSO

maw-peek(1), maw-broadcast(1), maw-send-enter(1), maw-locate(1), maw-wake(1), maw-health(1)

Repo: https://github.com/Soul-Brews-Studio/maw-js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment