Project Folders¶
Multi-project support for ClawMux. Each project gets its own set of 7 agents, chat history, work directories, and context. Shipped in v0.5.x.
User Experience¶
- User clicks New Project in the ClawMux web UI
- Enters a project name (e.g. "zeuldocs", "clawmux-v2", "web-finder")
- A new project is created with all 7 default agents (Sky, Alloy, Sarah, Adam, Echo, Onyx, Fable)
- User can switch between projects — each project has independent agents, history, and state
- Projects can be created, renamed, archived, and deleted
Architecture¶
Previous State (pre-projects)¶
/tmp/clawmux-sessions/
├── af_sky/ ← one Sky, one Alloy, etc.
├── af_alloy/
├── af_sarah/
├── am_adam/
├── am_echo/
├── am_onyx/
└── bm_fable/
- One flat set of 7 sessions
- tmux sessions named
voice-sky,voice-alloy, etc. - Session IDs are global:
voice-{name} - No concept of projects
Current State (shipped)¶
/tmp/clawmux-sessions/
├── default/
│ ├── af_sky/
│ ├── af_alloy/
│ └── ...
├── zeuldocs/
│ ├── af_sky/
│ ├── af_alloy/
│ └── ...
└── clawmux-v2/
├── af_sky/
├── af_alloy/
└── ...
- Sessions nested under project directories
- tmux sessions namespaced:
default-sky,zeuldocs-sky, etc. - Session IDs include project:
{project}-{name} - Each project has independent CLAUDE.md, .mcp.json, history
Data Model¶
Project¶
@dataclass
class Project:
name: str # URL-safe slug (e.g. "zeuldocs")
display_name: str # Human name (e.g. "Zeul Docs")
created_at: float
work_dir: str # /tmp/clawmux-sessions/{name}/
active: bool = True # False = archived
Session Changes¶
@dataclass
class Session:
# Existing fields...
project: str = "default" # Which project this session belongs to
Session ID format changes from voice-{name} to {project}-{name}.
Storage¶
{
"projects": [
{
"name": "default",
"display_name": "Default",
"created_at": 1709500000,
"active": true
},
{
"name": "zeuldocs",
"display_name": "Zeul Docs",
"created_at": 1709500100,
"active": true
}
],
"active_project": "default"
}
History Store¶
The existing HistoryStore needs project namespacing:
UI¶
Project Selector (Top Bar)¶
┌─────────────────────────────────────────────┐
│ ClawMux [▼ Default Project] [+] [⚙] │
├─────────────────────────────────────────────┤
│ ┌─────────────────────────┐ │
│ │ ● Default Project ✓ │ │
│ │ ○ Zeul Docs │ │
│ │ ○ ClawMux v2 │ │
│ │ ─────────────────────── │ │
│ │ + New Project... │ │
│ │ ⚙ Manage Projects │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────┐ ┌───────┐ ┌───────┐ ┌──────┐ │
│ │ Sky │ │ Alloy │ │ Sarah │ │ Adam │ ... │
│ └─────┘ └───────┘ └───────┘ └──────┘ │
└─────────────────────────────────────────────┘
- Dropdown in the top bar shows all projects
- Active project has a checkmark
- Quick-create button [+] next to the dropdown
- Agent cards below show only the agents for the selected project
New Project Dialog¶
┌──────────────────────────────┐
│ New Project │
│ │
│ Name: [________________] │
│ │
│ Work Dir (optional): │
│ [________________________] │
│ │
│ [Cancel] [Create] │
└──────────────────────────────┘
- Name is required, auto-slugified for directory names
- Optional work directory lets the user point to an existing repo
- If no work dir specified, uses
/tmp/clawmux-sessions/{slug}/
Manage Projects¶
┌──────────────────────────────────────┐
│ Manage Projects │
│ │
│ Default Project [Archive] │
│ 7 agents · active │
│ │
│ Zeul Docs [Archive] │
│ 7 agents · active │
│ │
│ ClawMux v2 [Delete] │
│ 0 agents · archived │
│ │
└──────────────────────────────────────┘
API Changes¶
New Endpoints¶
| Method | Path | Description |
|---|---|---|
| GET | /api/projects |
List all projects |
| POST | /api/projects |
Create a new project |
| PUT | /api/projects/{name} |
Update project (rename, archive) |
| DELETE | /api/projects/{name} |
Delete project and all its sessions |
| POST | /api/projects/{name}/activate |
Switch active project |
Modified Endpoints¶
| Method | Path | Change |
|---|---|---|
| GET | /api/sessions |
Returns sessions for active project only |
| POST | /api/sessions |
Creates session in active project |
WebSocket /ws |
project field added to session state messages |
WebSocket Protocol Addition¶
New message type for project switching:
Hub responds with updated session list for the new project.
Resource Management¶
Scaling Concern¶
Each project = up to 7 tmux sessions = up to 7 Claude Code instances.
3 projects × 7 agents = 21 concurrent Claude processes. This is heavy.
Strategy: Lazy Spawning¶
Don't spawn all 7 agents when a project is created. Spawn on demand:
- Project created → 0 agents running. Just the project entry in
projects.json. - User clicks an agent card → That agent spawns for this project.
- Agent idles for N minutes → Session suspends (tmux stays, Claude exits). History preserved for
--resume. - User clicks suspended agent → Claude resumes in existing tmux.
This means a user with 5 projects but only actively using 1 will have ~7 processes, not 35.
Session Lifecycle¶
Suspended = tmux session alive but Claude process exited. Work dir and history intact. Resume is instant via claude --resume.
Limits¶
- Configurable max concurrent sessions across all projects (default: 14 = 2 full projects)
- When limit hit, oldest idle session auto-suspends
- Warning shown in UI when approaching limit
Implementation Steps¶
Phase 1: Backend Data Model¶
- Create
ProjectManagerclass inserver/project_manager.py - Add
projects.jsonread/write - Modify
SessionManagerto accept project context - Namespace work dirs:
/tmp/clawmux-sessions/{project}/{voice_id}/ - Namespace tmux sessions:
{project}-{voice_name} - Namespace history store paths
Phase 2: API Layer¶
- Add project CRUD endpoints to
hub.py - Modify session endpoints to filter by active project
- Add
switch_projectWebSocket message handler - Add
projectfield to session state broadcasts - Migration: wrap existing sessions in a "default" project on first boot
Phase 3: Frontend — Project Selector¶
- Add project dropdown component to top bar
- Implement project switching (WebSocket message)
- Update agent grid to show only active project's agents
- Add "New Project" quick-create flow
Phase 4: Frontend — Project Management¶
- Full project management dialog
- Archive/unarchive projects
- Delete project (with confirmation — destroys all sessions and history)
- Rename project
Phase 5: Lazy Spawning & Suspension¶
- Implement session suspension (exit Claude, keep tmux)
- Implement resume-from-suspended
- Add concurrent session limit with auto-suspend
- UI indicators for suspended vs active vs spawning states
Phase 6: Per-Project CLAUDE.md¶
- Each project gets its own CLAUDE.md template
- Project-level context (repo path, instructions) injected into agent CLAUDE.md
- Users can customize per-project agent behavior
Migration¶
No auto-migration. The default project keeps the existing flat directory layout (/tmp/clawmux-sessions/{voice_id}/) forever. Named projects are opt-in and get their own subdirectories (/tmp/clawmux-sessions/{project}/{voice_id}/).
CRITICAL: Never move a live session directory. Claude Code sessions are tied to their exact working directory path. Moving a directory breaks bash, tools, and context permanently.
Recovery Plan¶
If deploying the project folders code breaks anything, run these exact commands:
1. Revert code¶
cd /home/zeul/GIT/clawmux
git checkout server/hub.py server/session_manager.py server/history_store.py
rm -f server/project_manager.py
2. Restart hub¶
3. Verify agents reconnect¶
sleep 30
curl -s http://localhost:3460/api/sessions | python3 -c "
import json, sys
for s in json.load(sys.stdin):
print(f'{s[\"voice\"]}: {s[\"status\"]}')
"
4. Verify agents can run commands¶
# Send a test message to any agent
curl -s -X POST http://localhost:3460/api/messages/send \
-H "Content-Type: application/json" \
-d '{"sender": "voice-sky", "to": "voice-onyx", "message": "Health check — can you run bash?"}'