Start Command
Overview
The symphony start command is the primary entry point for launching Symphony. It orchestrates initialization of both the web UI server and the orchestrator daemon, auto-detects projects in the workspace, and manages graceful lifecycle for both processes. Users run this once to bring the entire system online.
Usage
symphony start [--port <number>] [--codex | --claude]Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--port <number> | integer 1–65535 | 3000 | Web server port. Applies to both dev (nuxt dev --port) and production (Nitro) modes. Does not affect the orchestrator process. |
--codex | boolean | — | Use Codex CLI as the agent provider. Persisted to symphony.config.json; future symphony start runs without a provider flag retain this setting. Mutually exclusive with --claude. |
--claude | boolean | ✓ (implicit default) | Use Claude CLI as the agent provider. Persisted to symphony.config.json; future symphony start runs without a provider flag retain this setting. Mutually exclusive with --codex. |
Note:
--codexand--claudeare mutually exclusive. Passing both flags is an error. Omitting both on a subsequent run does not reset a previously persisted provider — it leavessymphony.config.jsonunchanged.
Examples
symphony start # Default: port 3000, Claude CLI
symphony start --port 4000 # Custom web server port
symphony start --codex # Switch to Codex CLI (persisted)
symphony start --claude # Switch back to Claude CLI (persisted)How It Works
Startup Sequence
1. PID Check The command reads .symphony/symphony.pid to check if Symphony is already running. If a PID exists and the process is alive (verified via process.kill(pid, 0)), startup fails with an error message directing the user to symphony status.
2. Database & Config Initialization
resetConfig() // Clear cached config
const config = loadConfig(symphonyDir)
const db = getDbForPath(config.db)
runMigrations(db)Migrations are idempotent, ensuring the schema is up-to-date for this run.
3. Project Auto-Detection The command scans the parent directory for projects using scanForProjects():
- Walks sibling directories
- Identifies projects by checking for
.git,package.json, etc. - Returns list with project name, type (nodejs, php, etc.), and path
4. Project SyncsyncProjects(db, detected) creates or updates project records in the database. This ensures the web UI has a current list of available projects without manual registration.
5. Dual-Process Spawning Two child processes are spawned via ProcessManager:
| Process | Dev Command | Prod Command | Ready Signal |
|---|---|---|---|
| Web | npx nuxt dev --port 3000 | node .output/server/index.mjs | Local: http or Listening |
| Orchestrator | npx tsx server/orchestrator/index.ts | npx tsx server/orchestrator/index.ts | [Orchestrator] Running |
Each process logs prefixed output: [web] ... and [orchestrator] ....
6. Readiness Detection The ProcessManager watches stdout for regex patterns (e.g., Local:\s+http for Nuxt). Once matched, the process is marked ready. If a process exits with non-zero code, all processes are shut down immediately.
7. Graceful Shutdown SIGINT (Ctrl+C) and SIGTERM signals trigger:
- Send SIGTERM to all managed processes
- Wait up to 30 seconds for graceful exit
- Force SIGKILL any remaining processes
- Remove PID file
Key Components
| File | Responsibility |
|---|---|
cli/commands/start.ts | Main start logic: PID check, DB init, project sync, process spawning |
cli/utils/processManager.ts | Child process spawning, stdout/stderr piping, readiness detection, graceful shutdown |
cli/utils/pid.ts | PID file I/O and process liveness checking |
cli/utils/output.ts | Terminal formatting (colors, tables, duration formatting) |
cli/index.ts | CLI routing and command dispatch |
Design Decisions
1. Why prevent double-start with PID files?
- Avoids port conflicts and orphaned processes
- Simple, OS-level mechanism (reliable across platforms)
- Alternative: DB flag — but filesystem PID file is more robust for process liveness
2. Why auto-detect projects instead of manual registration?
- Reduces setup friction for multi-project workspaces
- Keeps database in sync with reality automatically
- Fallback: manual
symphony listshows what was detected
3. Why spawn orchestrator via npx tsx?
- Allows running orchestrator during development (no build step required)
- In production, could be replaced with a precompiled Node binary
- Trades startup overhead for dev flexibility
4. Why readiness patterns instead of waiting N seconds?
- Regex-based readiness is reliable (catches actual startup completion)
- Avoids false positives from slow disks or CI environments
- Each process defines its own signal (Nuxt vs orchestrator differ)
5. Why 30-second shutdown timeout?
- Balances user patience (Ctrl+C should be fast) with graceful shutdown (agents commit work)
- Long enough for agent processes to finish current work
- SIGKILL fallback ensures cleanup on stuck processes
6. Why manage both web + orchestrator in one command?
- Simplifies user experience (one command starts everything)
- Ensures orchestrator is always running when web UI is up
- Could be split into separate commands, but coordination becomes harder
Related Docs
- Status Command — Querying runtime state
- Orchestrator Tick Loop — What happens after startup
- Project Detection — How projects are discovered
Known Gaps
- Port conflicts: No check for pre-existing processes on the same port; startup will fail but error message is from Nuxt, not Symphony
- Orphaned PID files: If Symphony crashes, PID file remains; requires manual cleanup or
symphony scanto recover - Slow project sync: Large workspaces may stall on
scanForProjects()during startup - No multi-instance mode: Cannot run multiple Symphony instances in parallel (even on different ports)
- Process exit codes: If web or orchestrator crash, all processes die; no retry logic