Environment setup
Once your first workspace is running, this page covers how to make Mooj work with your repo: lifecycle scripts you can hook into workspace creation and removal, environment variables Mooj exposes, and how to use ports without collisions.
mooj.json
Section titled “mooj.json”A JSON file at the root of your main repo (not inside individual workspaces).
{ "setup": "bin/setup", "run": "bin/run", "archive": "bin/archive"}All three keys are optional, each holds a single shell command. Mooj runs them with the workspace as cwd and .moojenv already sourced. Keep the actual scripts under bin/ so they’re versioned with everything else.
Runs in the background after a new workspace is created. Install dependencies, copy env files into place, anything else a fresh checkout needs.
#!/usr/bin/env bashset -euo pipefail
# Reuse the env file from the main repocp "$MOOJ_REPO_PATH/.env" .env
# Install dependenciesnpm install# bundle install # Ruby# uv sync # Python (uv)# cargo fetch # Rust
# Provision a workspace-scoped database, run migrations, etc.# createdb "myapp_$MOOJ_WORKSPACE_NAME"# bin/rails db:prepareRuns when you click Run on a workspace. Conventionally your dev server. Bind to $MOOJ_PORT so each workspace’s server lands on its own port.
#!/usr/bin/env bashset -euo pipefail
PORT="$MOOJ_PORT" npm run dev# PORT="$MOOJ_PORT" bin/rails server # Rails# overmind start -p "$MOOJ_PORT" # Procfile-based (overmind/foreman)# docker compose up # docker compose (set ports via $MOOJ_PORT in compose)archive
Section titled “archive”Runs when you remove a workspace, before the worktree is deleted. Clean up anything you don’t want lingering on disk.
#!/usr/bin/env bashset -euo pipefail
# Reclaim disk spacerm -rf node_modules dist .next
# Drop the workspace-scoped database created in setup# dropdb --if-exists "myapp_$MOOJ_WORKSPACE_NAME"Environment variables
Section titled “Environment variables”Mooj writes a .moojenv file inside every workspace at ~/mooj/workspaces/<repo>/<codename>/.moojenv. It contains:
export MOOJ_WORKSPACE_PATH="…/<repo>/<codename>"export MOOJ_WORKSPACE_NAME="<codename>"export MOOJ_REPO_PATH="…/<main repo>"export MOOJ_REPO_NAME="<repo>"export MOOJ_PORT="<allocated port>"Where these are visible:
- Inside
setup/run/archive: always. Mooj sources.moojenvitself before running each lifecycle script, so your scripts can rely on$MOOJ_PORTand friends with no extra setup. - Inside terminal sessions you open in the workspace: only if you have direnv installed (see next section).
Recommended: direnv
Section titled “Recommended: direnv”Install direnv so the variables are in your environment when you open a terminal session in a workspace:
brew install direnvAdd the shell hook to your ~/.zshrc (or ~/.bashrc):
eval "$(direnv hook zsh)"Open a new terminal and you’re set. Mooj runs direnv allow on every workspace it creates, so there’s no extra trust prompt.
If your repo already has a tracked .envrc (for shared direnv config), Mooj won’t overwrite it — append this line so direnv picks up Mooj’s variables too:
source_env_if_exists .moojenvWithout direnv, source .moojenv yourself when you need the variables.
Each workspace reserves a block of 10 consecutive ports. MOOJ_PORT is the first; the rest are MOOJ_PORT+1 through MOOJ_PORT+9. Use them in your dev server config so workspaces don’t collide.
Worked example — a web app reading PORT and an API reading API_PORT:
PORT="$MOOJ_PORT" npm run dev # web on 10010API_PORT="$((MOOJ_PORT + 1))" bin/api # api on 10011What lives in your repo
Section titled “What lives in your repo”Mooj writes two files into every worktree: .moojenv and .envrc. Both are machine-local — paths and ports are specific to your machine — so they don’t belong in your repo. Add them to your .gitignore:
.moojenv.envrcWhether to share mooj.json and your lifecycle scripts is your call: track them in git to share with the team, or .gitignore them if they’re just for you.