Local dev stack manager CLI. Two runtime dependencies (commander, yaml), ~500 lines of TypeScript.
| Command | Purpose |
|---|---|
pnpm build |
Compile TypeScript to dist/ |
pnpm dev |
Watch mode compilation |
pnpm format |
Biome lint + format with auto-fix (--write --unsafe) |
pnpm test |
Biome check + Vitest unit tests |
pnpm test:watch |
Vitest in watch mode |
pnpm test runs Biome lint/format check followed by Vitest. Tests live in src/**/*.test.ts. Currently no tests exist, but the infrastructure is in place.
src/
├── cli.ts # Commander entrypoint — registers 6 subcommands
├── config.ts # Loads vivarium.json or package.json["vivarium"]
├── ports.ts # Deterministic port computation from index (0–99)
├── registry.ts # Project state management in ~/.local/share/vivarium/<name>/
├── compose.ts # Docker Compose YAML generation (structured objects → yaml library)
├── env.ts # .env generation (compose interpolation + per-package)
├── commands/
│ ├── setup.ts # Full setup flow (12 steps)
│ ├── teardown.ts # Full teardown + legacy .vivarium/ migration
│ ├── start.ts # docker compose up (no setup logic)
│ ├── stop.ts # docker compose down (no teardown logic)
│ ├── compose.ts # Pass-through to docker compose
│ └── mcp-proxy.ts # stdio→SSE bridge for MCP services
└── utils/
├── docker.ts # docker/docker compose exec helpers
├── logger.ts # ANSI color-coded logger
└── prerequisites.ts # docker + jq availability check
- Structured YAML generation. Compose files are built as plain objects and serialized via the
yamllibrary. - No unit tests.
pnpm testrunsbiome check(lint + format validation) but there are no unit/integration tests. - Zero-config port allocation.
ports.tscomputes all ports from a single index. MCP sidecars are only accessed within the Docker network (no host-bound port). - Registry = directories.
~/.local/share/vivarium/<project>/contains state.json, compose.yaml, .env. No files are written to the consuming project directory. - Convention-based env generation. Packages named
backendandfrontendget standard env vars automatically (env.ts). Customenventries in config override conventions.
- Biome for linting and formatting (2-space indent, single quotes, recommended rules, import organizing)
- TypeScript ESM (
"type": "module") - All imports use
.jsextension (required for ESM) - JSDoc on all exported functions
execFileSyncpreferred overexecSync(no shell injection risk)- Synchronous I/O throughout — no async, the CLI runs sequentially
The docs/ workspace is a Next.js site (fumadocs) serving the public documentation.
| Command | Run from | Purpose |
|---|---|---|
pnpm dev |
docs/ |
Dev server at localhost:3000 |
pnpm build |
docs/ |
Production build |
Content lives in docs/content/docs/:
reference/-- commands, configuration, environment varsguides/-- custom env, MCP integration, multi-projectinternals/-- architecture, port allocation, registry
Docs must be kept in sync with code changes. Any change to CLI behavior, configuration schema, commands, or environment variable conventions must include corresponding updates to the relevant files under docs/content/docs/.
- All commands receive
projectRoot(fromprocess.cwd()) and resolve the project name frompackage.json. If there's nopackage.json, it falls back topath.basename. autoAssignIndexinregistry.tsdoes NOT callwriteState— setup.ts callswriteStateafter services are running. The index is only "soft-claimed" until state is written.- The
composecommand usesallowUnknownOption()+allowExcessArguments()in commander to pass through arbitrary args to docker compose. - Port collision check in
autoAssignIndexcompares candidate ports against ALL ports of ALL claimed projects, not just same-service ports.