Claude Control Development Kickoff — A Multi-Session Claude Management System
Once I started using Claude Code seriously for day-to-day work, it became clear pretty quickly that a single session wasn't going to cut it. I constantly found myself needing to handle frontend changes, backend refactoring, and test writing all at the same time. That's what led me to build Claude Control — a multi-session Claude management system.
Structure of the First Commit
The initial project structure was straightforward: a single FastAPI server and a session manager class, nothing more.
from fastapi import FastAPI
from typing import Dict
import subprocess
import asyncio
app = FastAPI(title="Claude Control")
class SessionManager:
def __init__(self):
self.sessions: Dict[str, subprocess.Popen] = {}
async def create_session(self, session_id: str, workdir: str) -> dict:
process = subprocess.Popen(
["claude", "--json"],
cwd=workdir,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self.sessions[session_id] = process
return {"session_id": session_id, "status": "created"}
async def send_prompt(self, session_id: str, prompt: str) -> str:
process = self.sessions.get(session_id)
if not process:
raise ValueError(f"Session {session_id} not found")
process.stdin.write(f"{prompt}\n".encode())
process.stdin.flush()
output = process.stdout.readline().decode()
return output
manager = SessionManager()
Design Decisions I Wrestled With Early On
The biggest question was how to manage processes. Spawning the Claude CLI as a subprocess is simple, but if the process dies, recovery is painful and tracking state gets messy. I went with this approach to start, but eventually migrated to Redis-based state management.
The other concern was session isolation. Each session needed to operate independently in its own working directory — changes made to files in one session couldn't bleed into another.
@app.post("/sessions")
async def create_session(request: CreateSessionRequest):
session = await manager.create_session(
session_id=request.session_id,
workdir=request.workdir,
)
return {"data": session, "message": "Session created"}
@app.post("/sessions/{session_id}/prompt")
async def send_prompt(session_id: str, request: PromptRequest):
result = await manager.send_prompt(session_id, request.prompt)
return {"data": result}
Reflections
When I pushed that first commit, I genuinely wasn't sure this would turn into anything useful. But having lived with Claude Code firsthand, the need for multi-session support was undeniable — and over the course of 88 more commits, the system grew into something quite solid. The autonomous execution mode and the manager-worker pattern in particular were features I hadn't planned for at all when I started.