Skip to content

Latest commit

 

History

History
78 lines (61 loc) · 4.36 KB

File metadata and controls

78 lines (61 loc) · 4.36 KB

Vivarium

Local dev stack manager CLI. Two runtime dependencies (commander, yaml), ~500 lines of TypeScript.

Commands

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.

Architecture

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

Key Design Decisions

  • Structured YAML generation. Compose files are built as plain objects and serialized via the yaml library.
  • No unit tests. pnpm test runs biome check (lint + format validation) but there are no unit/integration tests.
  • Zero-config port allocation. ports.ts computes 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 backend and frontend get standard env vars automatically (env.ts). Custom env entries in config override conventions.

Conventions

  • Biome for linting and formatting (2-space indent, single quotes, recommended rules, import organizing)
  • TypeScript ESM ("type": "module")
  • All imports use .js extension (required for ESM)
  • JSDoc on all exported functions
  • execFileSync preferred over execSync (no shell injection risk)
  • Synchronous I/O throughout — no async, the CLI runs sequentially

Docs

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 vars
  • guides/ -- custom env, MCP integration, multi-project
  • internals/ -- 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/.

Gotchas

  • All commands receive projectRoot (from process.cwd()) and resolve the project name from package.json. If there's no package.json, it falls back to path.basename.
  • autoAssignIndex in registry.ts does NOT call writeState — setup.ts calls writeState after services are running. The index is only "soft-claimed" until state is written.
  • The compose command uses allowUnknownOption() + allowExcessArguments() in commander to pass through arbitrary args to docker compose.
  • Port collision check in autoAssignIndex compares candidate ports against ALL ports of ALL claimed projects, not just same-service ports.