Multi-agent coordination for agent-shell.
Two levels of coordination:
- Project Dispatchers - coordinate multiple agents within a single project
- Meta-Agent - oversees all your projects, receives status heartbeats, monitors everything
Plus:
- Inter-agent communication via CLI tools (
agent-shell-send,agent-shell-ask,agent-shell-search) - Task tracking in org files that show up in your Emacs agenda
- ICC logging - all agent-to-agent messages logged as JSONL
- Big red button -
M-x meta-agent-shell-big-red-buttonstops everything
First install meta-agent-shell from a local checkout or package recipe that includes
the non-Elisp files this repo needs (bin/, agent-overview.md, meta-claude.md,
shared-tools.md). Then add this to your Emacs config:
(use-package meta-agent-shell
:after agent-shell
:config
(setq meta-agent-shell-heartbeat-file "~/heartbeat.org")
;; Recommended keybindings under SPC o m
;; Unbind existing SPC o m (mu4e in Doom) first if needed
(define-key doom-leader-map "om" nil)
(map! :leader
(:prefix ("o m" . "meta-agent")
:desc "Meta-agent session" "m" #'meta-agent-shell-start
:desc "Project dispatcher" "d" #'meta-agent-shell-jump-to-dispatcher
:desc "Start heartbeat" "h" #'meta-agent-shell-heartbeat-start
:desc "Stop heartbeat" "H" #'meta-agent-shell-heartbeat-stop
:desc "Send heartbeat now" "s" #'meta-agent-shell-heartbeat-send-now
:desc "STOP ALL AGENTS" "!" #'meta-agent-shell-big-red-button)))A Quelpa example of including the non-Elisp files:
:files (:defaults
("bin" "bin/*")
"agent-overview.md"
"meta-claude.md"
"shared-tools.md"
"dispatcher-instructions.md")Then run:
M-x meta-agent-shell-setup
This:
- Creates
~/.claude-meta/directory - Syncs support scripts and prompts into
~/.meta-agent-shell/ - Adds the support script directory to Emacs
PATHandexec-path - Tells you what to add to your agent instructions file
By default, meta-agent-shell uses its own wrapper meta-agent-shell-default-start-function
to establish the expected behavior when starting meta sessions, dispatchers, and named agents.
For most custom behavior, keep the package-owned wrapper and use
meta-agent-shell-before-start-hook or meta-agent-shell-after-start-hook.
Package-managed starts bind meta-agent-shell-start-session-policy from
meta-agent-shell-default-session-policy, which defaults to auto. Set it to
nil to preserve the provider’s configured default session mode.
The wrapper binds dynamic context variables:
| Name | Description |
|---|---|
meta-agent-shell-start-context | Package-managed start context, typically meta, dispatcher, or named-agent. |
meta-agent-shell-start-directory | Directory the session will start in; the wrapper binds default-directory to this value before calling agent-shell-start. |
meta-agent-shell-start-buffer-name | Optional buffer name override inserted into the resolved agent-shell config before startup. |
meta-agent-shell-start-session-policy | Abstract startup policy, safe, auto, or aggressive. For package-managed starts, this comes from meta-agent-shell-default-session-policy and is translated through meta-agent-shell-session-mode-map for equivalent behavior across agents. |
meta-agent-shell-start-session-mode-id | Optional provider mode id override. When non-nil, this takes precedence over meta-agent-shell-start-session-policy and is passed through directly. |
meta-agent-shell-start-command-prefix | Optional command prefix to install for the new session, for example claudebox --bash -c. |
meta-agent-shell-start-path-resolver-function | Optional path resolver function installed for the new session buffer. |
meta-agent-shell-start-config | Base agent-shell config selected by the wrapper; hooks can replace or adjust it before buffer-name and session mode overrides are applied. |
(add-hook
'meta-agent-shell-before-start-hook
(defun my/meta-agent-shell-start-customizations ()
(pcase meta-agent-shell-start-context
('named-agent
(when (string-match-p "/secretary/" meta-agent-shell-start-directory)
(setq meta-agent-shell-start-session-policy 'safe)
(setq meta-agent-shell-start-command-prefix
'("claudebox" "--bash" "-c"))
(setq meta-agent-shell-start-path-resolver-function
#'agent-shell--resolve-devcontainer-path))
(when current-prefix-arg
(setq meta-agent-shell-start-session-mode-id 'plan)))
('meta
(setq meta-agent-shell-start-buffer-name "Meta Claude")))))
(add-hook
'meta-agent-shell-after-start-hook
(defun my/meta-agent-shell-dispatcher-bootstrap ()
(when (eq meta-agent-shell-start-context 'dispatcher)
(shell-maker-submit
:input "Read ~/.policies/policy.org for inter-agent policies."))))meta-agent-shell-start-function also allows one to replace the wrapper entirely.
| Keybinding | Command | Description |
|---|---|---|
SPC o m m | meta-agent-shell-start | Meta-agent session (start/switch) |
SPC o m d | meta-agent-shell-jump-to-dispatcher | Project dispatcher (start/switch) |
SPC o m h | meta-agent-shell-heartbeat-start | Start heartbeat timer |
SPC o m H | meta-agent-shell-heartbeat-stop | Stop heartbeat timer |
SPC o m s | meta-agent-shell-heartbeat-send-now | Send heartbeat immediately |
SPC o m ! | meta-agent-shell-big-red-button | STOP ALL AGENTS |
Use dispatchers when you want multiple agents working on a single project.
M-x meta-agent-shell-start-dispatcher
The dispatcher runs in your project directory and can spawn agents, assign tasks, and coordinate work.
M-x meta-agent-shell-jump-to-dispatcher
Jumps to the dispatcher for the current buffer’s project. If none exists, offers to create one.
Agents communicate with each other using these shell commands:
agent-shell-spawn "AgentName" "initial task" # spawn a new named agent
agent-shell-send "buffer-name" "message"
agent-shell-ask "buffer-name" "question" # reply routed back automatically
agent-shell-whoami # print own buffer name
agent-shell-search "pattern" # search all sessions for regexp
agent-shell-search "pattern" projectname # search specific project
agent-shell-note desc "note text" # leave timestamped note in .tasks/Agents are taught these tools via agent-overview.md (included in setup).
The CLI tools pass the shell’s PID ($$), and the system walks up the process tree to find the ACP client:
$$ (shell) → zsh → claude → node (ACP client)
Each agent-shell buffer owns an ACP client process. Match the PID, find the buffer. So agents identify themselves without knowing their buffer name.
Agents can leave timestamped notes for themselves or others:
agent-shell-note refactor "Edge case in parse_args needs handling"Notes go to .tasks/agent_<desc>.org and persist across sessions. Add .tasks/ to your org-agenda-files to see them in your agenda.
Use the meta-agent when you want oversight across multiple projects.
M-x meta-agent-shell-start
This starts a dedicated agent in ~/.claude-meta/ that can see all your active sessions.
The meta-agent receives periodic heartbeats with status updates from all your projects.
- Create a file with standing instructions:
echo "Check on agent progress. Alert me if any seem stuck." > ~/heartbeat.org- Configure:
(setq meta-agent-shell-heartbeat-file "~/heartbeat.org")
(setq meta-agent-shell-heartbeat-interval 900) ; seconds between heartbeats (default 15 min)
(setq meta-agent-shell-heartbeat-cooldown 300) ; delay after you message the meta-agent- Start:
M-x meta-agent-shell-heartbeat-start
Each heartbeat includes:
- List of all active sessions with status (working/idle)
- Recent output from watched projects (if configured)
- Your standing instructions from the heartbeat file
M-x meta-agent-shell-heartbeat-stop- stop the timerM-x meta-agent-shell-heartbeat-send-now- send one immediately
Edit ~/.meta-agent-shell/config.org to give the meta-agent persistent context. Use @file references (same syntax as heartbeat.org) to pull in external files:
# ~/.meta-agent-shell/config.org @/Users/you/notes/priorities.org @/Users/you/notes/team-conventions.org
Contents are expanded and included in each heartbeat message, so the meta-agent always sees the latest version.
All inter-agent communication logged to ~/.meta-agent-shell/logs/YYYY-MM-DD-icc.jsonl:
{"timestamp":"2026-02-06T10:00:15","type":"ask","from":"Dispatcher Agent @ myproject","to":"Refactor Agent @ myproject","message":"..."}Event types: send, ask, view, close, interrupt
./tools/icc-viz.py -o graph.png # communication graph ./tools/icc-viz.py --timeline # activity over time ./tools/icc-viz.py --stats # summary statistics
See tests/test-instructions.md for a manual test workflow you can give to an agent. Each test includes expected behavior so the agent can verify things work correctly.
These workflows generally require using bypassPermissions mode to be effective.
One option is to run everything (including your emacs) in a VM with limited credentials.
For a sketch of a VMless solutions, see docs/sandboxing.org for running workers in containers with restricted dispatcher permissions. Note: Still needs a tunnel from containerized agents back to the host dispatcher.
GPL-3.0

