A tool for spawning and managing multiple AI coding agents (Claude Code and OpenAI Codex) with a Docker-like architecture: thin CLI (map) + local daemon (mapd).
Run Claude Code and Codex agents in parallel
Map.demo.mp4
Sync gh projects / issues
map-gh-projects.mp4
Quick install (macOS and Linux):
curl -fsSL https://raw.githubusercontent.com/pmarsceill/mapcli/main/install.sh | bashThis installs both map and mapd to ~/.local/bin. Make sure this directory is in your PATH.
MAP requires the following tools to be installed and available in your PATH:
| Dependency | Required For | Version |
|---|---|---|
| git | Worktree isolation | 2.15+ (worktree support) |
| tmux | Agent session management | Any recent version |
| claude | Claude Code agents | Latest (optional if only using Codex) |
| codex | OpenAI Codex agents | Latest (optional if only using Claude) |
At least one of claude or codex must be installed depending on which agent type you want to use.
Installing Dependencies:
macOS (Homebrew)
brew install git tmux
# Claude CLI: https://docs.anthropic.com/en/docs/claude-code
# Codex CLI: https://github.com/openai/codexUbuntu/Debian
sudo apt update && sudo apt install git tmux
# Claude CLI: https://docs.anthropic.com/en/docs/claude-code
# Codex CLI: https://github.com/openai/codexFedora/RHEL
sudo dnf install git tmux
# Claude CLI: https://docs.anthropic.com/en/docs/claude-code
# Codex CLI: https://github.com/openai/codexArch Linux
sudo pacman -S git tmux
# Claude CLI: https://docs.anthropic.com/en/docs/claude-code
# Codex CLI: https://github.com/openai/codexManual installation:
Download the latest release from the releases page and extract the binaries to a directory in your PATH.
Build from source:
git clone https://github.com/pmarsceill/mapcli.git
cd mapcli
make build
# Binaries are in bin/MAP (Multi-Agent Platform) provides infrastructure for spawning and coordinating multiple AI coding agents. It supports both Claude Code and OpenAI Codex agents. The architecture separates concerns:
map- Lightweight CLI for spawning agents and monitoring their statusmapd- Daemon process that manages agent lifecycles and worktrees
make buildThis creates two binaries in bin/:
map- CLI toolmapd- Daemon
-
Start the daemon:
map up # Or run in foreground for debugging: map up -f -
Spawn agents:
# Spawn a Claude agent (default) map agent create # Spawn a Codex agent map agent create -a codex # Spawn with a prompt map agent create -p "Fix the bug in auth.go" # Spawn multiple Codex agents map agent create -n 3 -a codex
-
Monitor agents:
# List spawned agents map agent list # Watch agent output in real-time map agent watch # List worktrees map worktree ls
-
Stop the daemon:
map down
| Command | Description |
|---|---|
map up [-f] |
Start the daemon (foreground with -f) |
map down [-f] |
Stop the daemon (force immediate shutdown with -f) |
map clean |
Clean up orphaned processes, tmux sessions, and socket files |
map watch |
Stream real-time events from the daemon |
map config list |
List all configuration values |
map config get <key> |
Get a configuration value |
map config set <key> <value> |
Set a configuration value |
| Command | Description |
|---|---|
map agents |
List spawned agents (alias: map ag) |
map agent create [-a type] |
Spawn agents (claude or codex) |
map agent list |
List spawned agents (alias: ls, same as map agents) |
map agent kill <id> |
Terminate a spawned agent |
map agent kill --all |
Terminate all spawned agents |
map agent watch [id] |
Attach to agent's tmux session |
map agent watch -a |
Watch all agents in tiled tmux view |
map agent respawn <id> |
Restart agent in dead tmux pane |
map agent merge <id> |
Merge agent's worktree changes into current branch |
map agent merge <id> -k |
Merge agent's changes and kill the agent |
| Command | Description |
|---|---|
map worktree ls |
List agent worktrees (alias: list) |
map worktree cleanup |
Remove orphaned worktrees |
map worktree cleanup --agent <id> |
Remove worktree for a specific agent |
map worktree cleanup --all |
Remove all agent worktrees |
| Command | Description |
|---|---|
map task submit <description> |
Submit a new task for agent processing (alias: map tasks, map t) |
map task ls [-n limit] |
List all tasks with status (default limit: 20) |
map task show <id> |
Show detailed task information |
map task cancel <id> |
Cancel a pending or in-progress task |
map task sync gh-project <name> |
Sync tasks from a GitHub Project |
map task my-task |
Show the current task for this agent (by working directory) |
map task input-needed <id> <question> |
Request user input via GitHub issue |
MAP can spawn Claude Code or OpenAI Codex agents as subprocesses, with each agent optionally isolated in its own git worktree for safe concurrent work.
MAP supports two agent types via the -a flag:
| Type | CLI | Description |
|---|---|---|
claude |
Claude Code | Anthropic's Claude Code CLI (default) |
codex |
OpenAI Codex | OpenAI's Codex CLI |
Each agent receives a unique, human-friendly name based on its type:
- Claude agents: French-style names (e.g.,
jacques-bernard,marie-claire,philippe-martin) - Codex agents: California-style names (e.g.,
chad-stevenson,bryce-anderson,tyler-johnson)
Names are automatically generated and guaranteed unique within a session.
# Spawn a Claude agent (default)
map agent create
# Spawn a Codex agent
map agent create -a codex
# Spawn 3 Claude agents in parallel
map agent create -n 3
# Spawn 3 Codex agents in parallel
map agent create -n 3 -a codex
# Spawn with a specific prompt
map agent create -p "Fix the bug in auth.go"
map agent create -a codex -p "Implement the login feature"
# Spawn without worktree isolation (agents share working directory)
map agent create --no-worktree# List all spawned agents
map agent list
# Kill a specific agent
map agent kill claude-abc123
# Force kill (SIGKILL instead of SIGTERM)
map agent kill claude-abc123 --force
# Kill all agents
map agent kill --all
# Force kill all agents
map agent kill --all --forceWhen agents are spawned with worktree isolation (the default), each agent gets its own git worktree in ~/.mapd/worktrees/. This allows multiple agents to work on the same repository concurrently without conflicts.
Permission Bypass: By default, agents are started with permission-bypassing flags to enable autonomous operation:
- Claude:
--dangerously-skip-permissions - Codex:
--dangerously-bypass-approvals-and-sandbox
This is safe when using worktrees because each worktree is an isolated copy created by MAP. Use --require-permissions to restore standard permission prompts if needed.
# List all worktrees
map worktree ls
# Clean up orphaned worktrees (agents that have exited)
map worktree cleanup
# Clean up a specific agent's worktree
./bin/map worktree cleanup --agent <agent-id>
# Clean up all worktrees
map worktree cleanup --allWhen an agent completes work in its worktree, use map agent merge to bring those changes back to your main branch:
# Merge an agent's changes into your current branch
map agent merge <agent-id>
# Merge with a custom commit message for uncommitted changes
map agent merge <agent-id> -m "Agent completed feature X"
# Squash all agent commits into one
map agent merge <agent-id> --squash
# Stage changes without committing (for manual review)
map agent merge <agent-id> --no-commit
# Merge and kill the agent afterward
map agent merge <agent-id> -k
map agent merge <agent-id> --killThe merge command will:
- Commit any uncommitted changes in the agent's worktree (if any)
- Merge those changes into your current branch
- Optionally kill the agent after a successful merge (with
-kflag)
MAP includes a task routing system for distributing work to agents.
Tasks follow this lifecycle: PENDING → OFFERED → ACCEPTED → IN_PROGRESS → COMPLETED/FAILED/CANCELLED
Tasks can also enter WAITING_INPUT status when an agent needs user input. Once the user responds, the task returns to IN_PROGRESS.
# Submit a new task
map task submit "Fix the authentication bug in login.go"
# Submit with scope paths (limits where agent can work)
map task submit "Update API handlers" -p ./internal/api -p ./internal/handlers
# List all tasks
map task ls
# Show task details
map task show <task-id>
# Cancel a task
map task cancel <task-id>MAP can import tasks directly from GitHub Projects using the gh CLI:
# Sync tasks from a GitHub Project (uses @me as default owner)
map task sync gh-project "My Project"
# Sync from an organization's project
map task sync gh-project "Sprint Board" --owner myorg
# Preview what would be synced without making changes
map task sync gh-project "My Project" --dry-run
# Sync from a different source column
map task sync gh-project "My Project" --status-column "Ready"
# Move items to a different target column after sync
map task sync gh-project "My Project" --target-column "Assigned"
# Limit the number of items to sync
map task sync gh-project "My Project" --limit 5How it works:
- Finds the GitHub Project by name
- Fetches issues from the source status column (default: "Todo")
- Creates a MAP task for each issue
- Moves the issue to the target status column (default: "In Progress")
Requirements:
- The
ghCLI must be installed and authenticated (gh auth login) - The project must have a "Status" field with single-select options
| Flag | Default | Description |
|---|---|---|
--status-column |
Todo |
Source status column to sync from |
--target-column |
In Progress |
Target status column after task creation |
--owner |
@me |
GitHub project owner (user, org, or @me) |
--limit |
10 |
Maximum number of items to sync |
--dry-run |
false |
Preview without creating tasks or updating GitHub |
When tasks are synced from GitHub Projects, MAP tracks the originating issue and enables bidirectional communication:
Automatic Input Detection:
- The daemon monitors agent tmux sessions for signs that the agent is waiting for user input
- When detected (agent idle + question pattern in output), the question is automatically posted to the GitHub issue
- Users can respond directly on the GitHub issue
- Responses are automatically delivered back to the agent's session
How it works:
- Sync issues with
map task sync gh-project "Project"- GitHub metadata is stored with each task - Agent works on the task and asks a question (detected automatically)
- Question is posted to the GitHub issue with prefix
**My agent needs more input:** - User responds on GitHub
- Response is delivered to the agent's tmux session
- Agent continues working
Manual input requests:
Agents can also explicitly request input:
# From within an agent session, request user input
map task input-needed <task-id> "What error format should I use?"Agent introspection:
# Find the current task for this working directory
map task my-taskWatch real-time events from the daemon:
# Stream all daemon events
map watchEvents include task lifecycle changes (created, offered, accepted, started, completed, failed, cancelled, waiting_input, input_received) and agent status updates.
| Flag | Default | Description |
|---|---|---|
-a, --agent-type |
claude |
Agent type: claude or codex |
-n, --count |
1 |
Number of agents to spawn |
--branch |
current branch | Git branch for worktrees |
--worktree |
true |
Use worktree isolation |
--no-worktree |
false |
Skip worktree isolation |
--name |
agent type | Agent name prefix |
-p, --prompt |
none | Initial prompt to send to the agent |
--require-permissions |
false |
Require permission prompts (by default, permissions are skipped for autonomous operation) |
┌─────────┐ ┌─────────────────────────────────────┐
│ CLI │◄───────►│ Daemon (mapd) │
│ (map) │ gRPC │ ┌───────────┐ ┌────────────────┐ │
└─────────┘ │ │ Worktree │ │ Process │ │
│ │ Manager │ │ Manager │ │
│ └─────┬─────┘ └───────┬────────┘ │
└────────┼────────────────┼───────────┘
│ │
┌──────────────┘ └──────────────┐
▼ ▼
┌───────────────────┐ ┌─────────────────────┐
│ ~/.mapd/worktrees │ │ claude/codex CLI │
│ claude-abc123/ │◄─────── cwd ─────────│ (tmux session) │
│ codex-def456/ │ └─────────────────────┘
└───────────────────┘
mapcli/
├── cmd/
│ ├── map/ # CLI binary
│ └── mapd/ # Daemon binary
├── internal/
│ ├── daemon/ # Daemon core (server, store, process management)
│ ├── cli/ # CLI commands
│ └── client/ # Shared gRPC client
├── proto/map/v1/ # Protocol definitions & generated code
├── go.mod
├── Makefile
└── README.md
MAP supports persistent configuration via a YAML file at ~/.mapd/config.yaml. Configuration values can be set via:
- CLI flags (highest priority)
- Environment variables (with
MAP_prefix) - Config file (
~/.mapd/config.yaml) - Defaults (lowest priority)
| Command | Description |
|---|---|
map config list |
List all configuration values (alias: ls) |
map config get <key> |
Get a specific configuration value |
map config set <key> <value> |
Set and persist a configuration value |
# ~/.mapd/config.yaml
socket: /tmp/mapd.sock
data-dir: ~/.mapd
agent:
default-type: claude # claude or codex
default-count: 1 # number of agents to spawn
default-branch: "" # git branch for worktrees
use-worktree: true # worktree isolation
skip-permissions: true # skip permission prompts| Key | Default | Description |
|---|---|---|
socket |
/tmp/mapd.sock |
Unix socket path for daemon communication |
data-dir |
~/.mapd |
Data directory for SQLite and worktrees |
agent.default-type |
claude |
Default agent type (claude or codex) |
agent.default-count |
1 |
Default number of agents to spawn |
agent.default-branch |
"" |
Default git branch for worktrees (empty = current branch) |
agent.use-worktree |
true |
Use worktree isolation by default |
agent.skip-permissions |
true |
Skip permission prompts by default |
All configuration options can be set via environment variables with the MAP_ prefix. Nested keys use underscores:
export MAP_SOCKET=/custom/path.sock
export MAP_AGENT_DEFAULT_TYPE=codex
export MAP_AGENT_DEFAULT_COUNT=3| Flag | Default | Description |
|---|---|---|
-s, --socket |
/tmp/mapd.sock |
Unix socket path for daemon communication |
--config |
~/.mapd/config.yaml |
Path to config file |
| Flag | Default | Description |
|---|---|---|
-f, --foreground |
false |
Run daemon in foreground |
-d, --data-dir |
~/.mapd |
Data directory for SQLite |
- Go 1.24+
- All system requirements above
- (Optional) protoc with go plugins for regenerating proto files
If you have protoc installed:
make install-tools # Install protoc-gen-go plugins
make generate # Regenerate proto files| Target | Description |
|---|---|
make build |
Build all binaries |
make all |
Generate protos and build |
make generate |
Regenerate protobuf files |
make test |
Run all tests |
make clean |
Remove build artifacts |
make rebuild |
Clean, generate, and build |
make deps |
Download and tidy dependencies |
make install-tools |
Install protoc plugins |
make dev |
Hot reload development mode (requires Air) |
Run the linter before submitting changes:
golangci-lint run --timeout=5mCommon issues to watch for:
- errcheck: Always handle error return values (use
_ = fn()for intentionally ignored errors) - staticcheck: Avoid deprecated functions
- For deferred close operations:
defer func() { _ = x.Close() }()
Run components directly without building:
make run-daemon # Run daemon in current shell
make run-cli ARGS="agent list" # Run CLI with argumentsCore runtime dependencies:
| Package | Purpose |
|---|---|
github.com/spf13/cobra |
CLI framework |
github.com/spf13/viper |
Configuration management |
google.golang.org/grpc |
gRPC communication |
google.golang.org/protobuf |
Protocol buffer support |
modernc.org/sqlite |
SQLite database (pure Go) |
github.com/google/uuid |
UUID generation |