Note: Currently only tested on macOS & Claude Code.
Security Note: This tool is designed for local personal use only. It provides terminal access to your system and should not be deployed as a shared server or exposed to untrusted networks. The server binds to
localhostby default.
A web application for managing multiple AI coding agent instances running in different git worktrees. Control all your agents through a unified browser interface instead of scattered terminal windows.
Currently supports Claude Code as the default agent, with plans to support additional agents (Gemini CLI, Codex, etc.) in the future.
- Unified Dashboard: View and manage all repositories, worktrees, and agent sessions in one place
- Browser-based Terminal: Full terminal access via xterm.js - no need for separate terminal windows
- Session Persistence: Sessions continue running even when you close the browser tab (tmux-like behavior)
- Multiple Workers per Session: Run agent and terminal workers side-by-side within a single session
- Inter-worker Messaging: Send messages between workers in the same session via an embedded message panel
- Git Worktree Integration: Create and delete git worktrees directly from the UI
- Real-time Updates: WebSocket-based notifications for session and worker lifecycle changes
- Orchestration via MCP: Agents can delegate tasks, coordinate workflows, and manage other agents programmatically via MCP tools
- Session: A working context tied to a git worktree or arbitrary directory. Each session can have multiple workers.
- Worker: A PTY process running within a session. Two types:
- Agent Worker: Runs an AI agent (e.g., Claude Code)
- Terminal Worker: A plain terminal shell
Backend (Bun + Hono) Frontend (React + Vite)
┌──────────────────────────┐ ┌──────────────────────────┐
│ SessionManager │ │ Dashboard │
│ ├── Session1 │ │ ├── SessionList │
│ │ ├── AgentWorker │◄──────►│ │ └── WorkerTabs │
│ │ └── TerminalWorker │ WS │ └── Terminal (xterm.js) │
│ └── Session2 │ │ │
│ └── AgentWorker │ │ │
└──────────────────────────┘ └──────────────────────────┘
-
Bun >= 1.3.5
scripts/check-bun-version.mjsis wired as apreinstallhook and hard-fails on older Bun, so the requirement is enforced rather than advisory. The floor is set by two features:Bun.Terminal(used by the server, 1.3.5+) andminimumReleaseAge(the supply-chain age gate, 1.3.0+); the higher of the two wins. See Supply Chain Security for the age gate details.
# Install dependencies
bun install
# Start development servers (frontend + backend)
bun devThe development server runs at:
- Frontend: http://localhost:5173
- Backend: http://localhost:3457
When running multiple development instances (e.g., in different git worktrees), you can use a .env file to avoid port and data conflicts.
# Copy the example file
cp .env.example .env
# Edit .env with your settingsAvailable variables:
| Variable | Default | Description |
|---|---|---|
PORT |
3457 |
Backend server port |
CLIENT_PORT |
5173 |
Frontend dev server port |
AGENT_CONSOLE_HOME |
~/.agent-console-dev |
Data directory (DB, outputs) |
Bun automatically loads .env files - no additional packages required.
bun run buildThis creates a production bundle in the dist/ directory:
dist/
├── package.json # Standalone package manifest
├── index.js # Bundled server
└── public/ # Built frontend assets
# From the project root (after build)
bun startOr run directly:
NODE_ENV=production bun dist/index.jsThe server runs at http://localhost:3457
A deployment script is provided to install Agent Console as a macOS Launch Agent (auto-start on login):
# Deploy with defaults (port 6340)
./scripts/update-and-deploy-for-mac.sh
# Deploy with custom port
PORT=8080 APP_URL=http://localhost:8080 ./scripts/update-and-deploy-for-mac.shThe script builds the project, copies files to ~/.agent-console/server/, installs a launchd plist, and starts the service.
The Launch Agent loads ~/.agent-console/.env on every startup. This is the recommended way to configure the server in production — especially for secrets that should not be embedded in the plist file.
# Example: ~/.agent-console/.env
GITHUB_WEBHOOK_SECRET=your-secret-here
LOG_LEVEL=infoAfter editing .env, restart the service to apply changes (no redeployment required):
launchctl kickstart -k "gui/$(id -u)/com.agent-console"Available server environment variables are defined in packages/server/src/lib/server-config.ts.
Note: Variables set in
.envoverride values from the launchd plist.PORT,APP_URL,NODE_ENV, andPATHare set in the plist at deploy time; all other variables should be configured via.env.
This repo uses Bun's minimumReleaseAge install setting to refuse npm package versions younger than 7 days. Compromised packages are usually detected or unpublished within a few days of release, so a short cool-off window catches the common case while letting routine updates through.
Configuration lives in bunfig.toml at the repo root. A preinstall hook (scripts/check-bun-version.mjs) enforces the repo's minimum Bun version, which is at or above the 1.3.0 floor required by minimumReleaseAge, so the age gate is never silently ignored.
When you genuinely need a just-published version (e.g., a CVE patch), bypass the gate for that single command rather than editing bunfig.toml:
# CLI flag, scoped to one invocation:
bun add <pkg> --minimum-release-age 0
# Or via environment variable:
BUN_CONFIG_MINIMUM_RELEASE_AGE=0 bun installRecord the reason (CVE number, advisory link, etc.) in the commit message or PR description so the override is auditable. Do not lower minimumReleaseAge in bunfig.toml — keep the gate intact and override per-command.
The dist/ directory can be distributed independently. Users only need to:
cd dist
bun install # Installs only bun-pty (~few seconds)
bun start # Starts the serverWhen creating a new worktree, Agent Console can automatically copy template files into it. This is useful for setting up consistent configurations (e.g., .claude/settings.local.json for MCP servers) across all worktrees.
Templates are searched in the following order (first found wins):
- Repository-local:
.agent-console/directory in the repository root - Global:
$AGENT_CONSOLE_HOME/repositories/<owner>/<repo>/templates/
The default $AGENT_CONSOLE_HOME is ~/.agent-console.
Place files in the templates directory with the same structure you want in the worktree:
~/.agent-console/repositories/<owner>/<repo>/templates/
├── .claude/
│ └── settings.local.json # → copied to <worktree>/.claude/settings.local.json
├── .env.local # → copied to <worktree>/.env.local
└── any/
└── nested/
└── file.txt # → copied to <worktree>/any/nested/file.txt
Template files support variable substitution using {{VARIABLE}} syntax:
| Placeholder | Description | Example |
|---|---|---|
{{WORKTREE_NUM}} |
Worktree index number (0, 1, 2, ...) | 1 |
{{BRANCH}} |
Branch name | feature/add-login |
{{REPO}} |
Repository name (without owner) | agent-console |
{{WORKTREE_PATH}} |
Full path to the worktree | /Users/me/.agent-console/.../wt-001-abc |
{{WORKTREE_NUM}} supports arithmetic operations:
| Expression | Description | Example (WORKTREE_NUM=2) |
|---|---|---|
{{WORKTREE_NUM + 3000}} |
Addition | 3002 |
{{WORKTREE_NUM - 1}} |
Subtraction | 1 |
{{WORKTREE_NUM * 10}} |
Multiplication | 20 |
{{WORKTREE_NUM / 2}} |
Division (floor) | 1 |
To configure different ports for each worktree's development server (.env.local):
# Worktree: {{BRANCH}}
DEV_PORT={{WORKTREE_NUM + 3000}}
API_PORT={{WORKTREE_NUM + 4000}}With this template, worktree 0 uses DEV_PORT=3000, worktree 1 uses DEV_PORT=3001, and so on.
Agent Console exposes MCP (Model Context Protocol) tools that turn any AI agent into an orchestrator. A single agent can delegate tasks to new worktrees, monitor their progress, coordinate inter-agent communication, run review workflows, and manage timers — all programmatically through a unified tool interface.
Register the Agent Console MCP server in Claude Code (one-time):
# For production (default port 6340) for user scope
claude mcp add --scope user --transport http agent-console http://localhost:6340/mcp
# For production (default port 6340) for local scope
claude mcp add --transport http agent-console http://localhost:6340/mcp
# For development (if using a different port, match your .env PORT) for local scope
claude mcp add --transport http agent-console-dev http://localhost:3457/mcpThis adds the MCP server to ~/.claude.json. All Claude Code instances (including those spawned by Agent Console) will automatically discover the tools.
Tip: If you change the server port, update the URL accordingly. Multiple Agent Console instances can coexist with different names (e.g.,
agent-console,agent-console-2).
| Tool | Description |
|---|---|
list_sessions |
List all active sessions with worker activity states |
get_session_status |
Get a specific session's status, workers, and parent info |
close_session |
Close a session and clean up its workers |
| Tool | Description |
|---|---|
delegate_to_worktree |
Create a worktree + session + agent and send a prompt — the primary orchestration tool |
remove_worktree |
Remove a git worktree and its associated session |
| Tool | Description |
|---|---|
send_session_message |
Send a message to a worker in another session (file-based with PTY notification) |
write_memo |
Write a Markdown memo for a session, visible in the UI |
| Tool | Description |
|---|---|
create_timer |
Create a periodic timer that sends notifications to a worker at specified intervals |
delete_timer |
Delete a periodic timer |
list_timers |
List active timers, optionally filtered by session |
| Tool | Description |
|---|---|
run_process |
Start an interactive script that communicates via STDOUT/STDIN |
write_process_response |
Send a response to a waiting interactive process |
kill_process |
Terminate a running interactive process |
list_processes |
List all interactive processes (useful after agent restart) |
| Tool | Description |
|---|---|
write_review_annotations |
Write review annotations for a git-diff worker, pushed to the client in real-time |
clear_review_annotations |
Clear all review annotations for a git-diff worker |
| Tool | Description |
|---|---|
list_agents |
Discover available agents (built-in + custom) with capabilities |
list_repositories |
Discover available repositories with IDs and remote URLs |
An orchestrator splits work across multiple worktrees and monitors progress:
Orchestrator (wt-000)
├── delegate_to_worktree → Agent A (wt-001): "implement feature X"
├── delegate_to_worktree → Agent B (wt-002): "write tests for module Y"
└── delegate_to_worktree → Agent C (wt-003): "fix bug Z"
... later ...
├── get_session_status(wt-001) → "active, working"
├── get_session_status(wt-002) → "idle, waiting for input"
└── get_session_status(wt-003) → "idle, task complete"
With parentSessionId and parentWorkerId, delegated agents automatically report results back via send_session_message.
Set up periodic check-ins to monitor long-running tasks:
create_timer(sessionId, workerId, intervalSeconds: 300, action: "check CI status and report")
→ Timer fires every 5 minutes with [internal:timer] PTY notification
→ Agent wakes up, checks status, takes action
delete_timer(timerId) # Clean up when done
Drive scripts that require back-and-forth communication:
run_process(command: "node acceptance-check.js 526", sessionId, workerId)
→ Process starts, sends STDOUT as [internal:process] PTY notifications
→ Agent reads output, decides next action
write_process_response(processId, content: "approve")
→ Process receives response, continues execution
kill_process(processId) # Terminate if needed
Annotate diffs for human review and leave persistent notes:
write_review_annotations(workerId, sessionId, annotations: [...], summary: {...})
→ Annotations appear in the git-diff viewer in real-time
write_memo(sessionId, content: "## Status\n- Feature complete\n- Tests passing\n- Ready for review")
→ Memo visible in the session UI panel
See docs/design/self-worktree-delegation.md for the full design document.
Agent Console can receive GitHub webhooks and route them to active sessions. When a webhook arrives (e.g., CI failure, PR merged), Agent Console:
- Matches the event to sessions working on the same repository
- Notifies agent workers by writing a structured message to the agent's PTY input
- Sends UI notifications via WebSocket to connected browsers
-
Set the webhook secret environment variable:
GITHUB_WEBHOOK_SECRET=your-secret-here
For the macOS Launch Agent, add it to
~/.agent-console/.env. -
Expose the webhook endpoint to the internet:
GitHub sends webhooks from its servers, so your local Agent Console must be reachable via a public URL. Use a tunnel service such as ngrok, Cloudflare Tunnel, or similar:
# Example with ngrok (forward to the Agent Console server port) ngrok http 3457 # → https://xxxx-xxxx.ngrok-free.app
-
Configure the webhook in GitHub:
- Go to Settings > Webhooks > Add webhook in your repository (or organization)
- Payload URL:
https://<your-tunnel-domain>/webhooks/github - Content type:
application/json - Secret: Same value as
GITHUB_WEBHOOK_SECRET - Events: Select individual events:
Workflow runs,Issues,Pull requests
Place the secret in a file outside the deploy target so that the rsync --delete in scripts/update-and-deploy-for-multiuser-ubuntu.sh does not wipe it on the next redeploy. The recommended path is /home/<service-user>/.config/agent-console/secrets.env, owned by the service user with mode 0600. Then point the systemd unit at it via EnvironmentFile=-.
# 1. Secret file outside the deploy target
sudo mkdir -p /home/agentconsole/.config/agent-console
echo "GITHUB_WEBHOOK_SECRET=$(openssl rand -hex 32)" | \
sudo tee /home/agentconsole/.config/agent-console/secrets.env
sudo chown -R agentconsole:agentconsole /home/agentconsole/.config/agent-console
sudo chmod 600 /home/agentconsole/.config/agent-console/secrets.env
# 2. Reference from the systemd unit
sudo systemctl edit --full agent-console.service
# In [Service], add:
# EnvironmentFile=-/home/agentconsole/.config/agent-console/secrets.env
# The leading `-` makes the unit start even if the file is absent.
# 3. Reload + restart
sudo systemctl daemon-reload
sudo systemctl restart agent-console.service
# 4. Verify (see "Verifying webhook configuration" below for the auth-cookie variant)
# From a logged-in browser DevTools console:
# fetch('/api/system/health').then(r => r.json()).then(console.log)
# → { webhookSecretConfigured: true, ... }Why outside the deploy target. scripts/update-and-deploy-for-multiuser-ubuntu.sh syncs the source-repo into the deploy target with rsync -a --delete, excluding only node_modules and .git. A secret file placed at <deploy-target>/.env would work on the first deploy (Bun auto-loads env files at WorkingDirectory) but would be deleted on the next update-and-deploy-for-multiuser-ubuntu.sh invocation, because env files are excluded from the source-repo by .gitignore. Placing the secret under /home/<service-user>/.config/agent-console/ and referencing it via EnvironmentFile=- keeps deploys and secret management on independent lifecycles.
Tunneling for inbound webhooks (Cloudflare Tunnel, ngrok, etc.) works the same as in the single-user instructions above — point the public URL at the server's HOST:PORT (e.g. 127.0.0.1:8080 for the bootstrap default).
| GitHub Event | Condition | Inbound Event | Actions |
|---|---|---|---|
| Workflow runs | Completed successfully | ci:completed |
Notify agent, refresh diff view |
| Workflow runs | Completed with failure | ci:failed |
Notify agent, show UI alert |
| Issues | Closed | issue:closed |
Show UI alert |
| Pull requests | Merged | pr:merged |
Refresh diff view, show UI alert |
- Events are matched to sessions by comparing the webhook's repository name with each session's git remote URL
- If the event includes a branch name, only sessions working on that branch are notified
- Events without a branch (e.g., issue closed) are delivered to all sessions for that repository
| Variable | Required | Default | Description |
|---|---|---|---|
GITHUB_WEBHOOK_SECRET |
Yes | (empty) | Shared secret for HMAC-SHA256 signature verification. If unset, incoming webhooks are accepted but not processed (the endpoint still returns 200 OK). Check server logs or GET /api/system/health to verify configuration. |
GET /api/system/health returns { webhookSecretConfigured: boolean, appUrl: string | null } for the running server. This endpoint requires authentication — unauthenticated requests return 401 Unauthorized. Two reliable ways to call it:
- From a logged-in browser DevTools console:
fetch('/api/system/health').then(r => r.json()).then(console.log)
- With curl, providing the auth cookie: Copy the
auth_token=...cookie value from your browser's DevTools (Application → Cookies) and pass it via--cookie. The cookie name isauth_token(defined inpackages/server/src/lib/auth-constants.ts):curl -s --cookie "auth_token=<your-auth-cookie-value>" http://localhost:8080/api/system/health | jq
A simple unauthenticated curl -sf http://localhost:8080/api/system/health returns an empty body (silent failure on 401), which is easy to misread as "the webhook is unconfigured." Use one of the authenticated paths above.
agent-console/
├── packages/
│ ├── client/ # React frontend
│ ├── server/ # Hono backend
│ └── shared/ # Shared TypeScript types
├── docs/ # Documentation
├── scripts/ # Deployment scripts
└── dist/ # Production build output (generated)
See AGENTS.md for repository guidelines, commands, and testing expectations.
- Backend: Bun, TypeScript, Hono, bun-pty
- Frontend: React, TypeScript, Vite, TanStack Router, TanStack Query, xterm.js, Tailwind CSS
- Build: Bun bundler (server), Vite (frontend)
- Package Manager: Bun workspaces
This project serves as a testbed for exploring the boundaries of AI-assisted software development. The goal is to minimize human involvement throughout the entire development lifecycle:
- Code generation: All code is written by Claude Code, not by humans
- Code review: Reviews are performed by Claude subagents (code-quality-reviewer, test-reviewer, ux-architecture-reviewer)
- Testing: Test code is also generated and reviewed by AI
The human role is limited to:
- Providing high-level requirements and direction
- Final approval of pull requests
- Resolving issues that AI cannot handle autonomously
This approach allows us to dogfood Agent Console while simultaneously validating how far AI can go in autonomous software development. The codebase you see here is the result of this experiment.
This project is built on the shoulders of amazing open-source projects:
- Claude Code - The AI coding agent that wrote most of this codebase with remarkable speed and quality. This project literally couldn't exist without it.
- Bun - Blazing fast runtime that makes development a joy
- Hono - Ultrafast web framework with excellent DX
- TypeScript - Type safety that saves countless debugging hours
- xterm.js - The terminal emulator that makes browser-based CLI possible
- Vibe Kanban - A fantastic project for managing AI coding agents. Exploring this project sparked the idea for Agent Console. Highly recommended!
Thank you to all the maintainers and contributors!
MIT