Local Dev Setup
This is a start-to-finish runbook for building and launching Tranquil Studio from source on macOS. It is written to be executed top to bottom — you can follow it yourself, or point an LLM agent harness (e.g. Claude Code) at this page and let it run each step.
Tranquil Studio (tranquil-client) is a fork of Pulsar (an
Atom/Electron editor). Its owned features live in eleven sibling packages, plus two forked
third-party packages, symlinked into the app for live editing. This runbook clones all fourteen
repos, wires them together, and launches the real Electron GUI.
Prerequisites
Before starting, make sure the shell has:
- git, with access to the private
tranquillabs/*repos — an SSH key or a token that can clone them. All thegit clonecommands below fail without it. - Node via nvm — each repo pins its version in
.nvmrc, sonvm useselects it. Node ≥ 18 (nvm typically resolves to 20.x here). - Yarn 1 (classic) —
yarn --versionshould report1.x. Yarn 1’slink:feature is what creates the live sibling symlinks; Yarn 2+ won’t work. - Deno — automations run as Deno subprocesses
(ADR-0022), so without it the app launches
normally but no automation runs. It is also the only tooling
tranquil-test-suiteuses. No binary is bundled yet, so the app resolves one from thetranquil-automations.denoPathsetting, thenPATH, then~/.deno/bin/deno,/opt/homebrew/bin/denoand/usr/local/bin/deno. Install and verification are in Run the Test Suite.
Everything is cloned under one workspace root. This runbook uses ~/Documents/Tranquil/Repos — substitute your own path consistently if you prefer another.
Clone the repos
Clone tranquil-client (the app) plus its eleven owned packages and two forked third-party
packages as siblings in the same directory. They must sit next to each other — the link:../ paths in tranquil-client/package.json resolve relative to the repo root.
mkdir -p ~/Documents/Tranquil/Repos
cd ~/Documents/Tranquil/Repos
git clone https://github.com/tranquillabs/tranquil-client.git
git clone https://github.com/tranquillabs/tranquil-rpc.git
git clone https://github.com/tranquillabs/tranquil-automations.git
git clone https://github.com/tranquillabs/tranquil-browser.git
git clone https://github.com/tranquillabs/tranquil-config.git
git clone https://github.com/tranquillabs/tranquil-debug.git
git clone https://github.com/tranquillabs/tranquil-drag-drop.git
git clone https://github.com/tranquillabs/tranquil-window-color.git
git clone https://github.com/tranquillabs/tranquil-tips.git
git clone https://github.com/tranquillabs/tranquil-theme-icons.git
git clone https://github.com/tranquillabs/tranquil-business-dark.git
git clone https://github.com/tranquillabs/tranquil-business-light.git
git clone https://github.com/tranquillabs/atom-language-csv.git
git clone https://github.com/tranquillabs/atom-pdf-view.git The resulting layout:
~/Documents/Tranquil/Repos/
tranquil-client/ ← main app (Pulsar fork)
tranquil-rpc/ ← guest↔host RPC layer (Cap'n Web)
tranquil-automations/ ← automation runner + tree-view/tab UI
tranquil-browser/ ← embedded Chromium browser
tranquil-config/ ← config, defaults, and the settings tab
tranquil-debug/ ← breakpoints and step debugging for .ts automations
tranquil-drag-drop/ ← drag tabs to the tree to save .url files
tranquil-window-color/ ← per-window accent color
tranquil-tips/ ← background tips when no editors are open
tranquil-theme-icons/ ← semantic file-type icons
tranquil-business-dark/ ← default dark UI theme
tranquil-business-light/ ← default light UI theme
atom-language-csv/ ← forked third-party CSV grammar (npm name: language-csv)
atom-pdf-view/ ← forked third-party PDF preview pane (npm name: pdf-view) All repos are on the main branch.
Install dependencies
Install tranquil-client, initialize the ppm submodule, then install each sibling package
that has its own dependencies. The per-sibling installs matter: a symlinked package resolves its
own require()s from its real path, so it needs a local node_modules/ to find them.
pdf-view pulls in real runtime dependencies (pdfjs-dist, atom-space-pen-views, and others) and
has no .nvmrc of its own, so install it first, with the Node version pinned explicitly rather than
relying on whatever nvm last had active:
cd ~/Documents/Tranquil/Repos/atom-pdf-view
source ~/.nvm/nvm.sh && nvm use 20.16.0
yarn install
cd .. cd ~/Documents/Tranquil/Repos/tranquil-client
source ~/.nvm/nvm.sh && nvm use
yarn install
git submodule update --init ppm
cd ppm && yarn install && cd ..
# Install deps inside each sibling package that has its own (so it can resolve them)
cd ../tranquil-rpc && yarn install
cd ../tranquil-automations && yarn install
cd ../tranquil-browser && yarn install
cd ../tranquil-config && yarn install
cd ../tranquil-tips && yarn install
cd ../tranquil-client The theme, icon, window-color, drag-drop, debug, language-csv, and examples packages have
no dependencies of their own, so they need no yarn install. (tranquil-debug reaches the
automation runner through an Atom service, not an npm dependency, which is why it needs none; language-csv is a grammar-only package with no code to run at all.)
tranquil-rpcis bundled. The app loads its committeddist/host.js(built fromlib/by esbuild), notlib/directly. A fresh clone already shipsdist/, so it launches as-is — but if you later edittranquil-rpc/lib/, runyarn buildin that repo to regeneratedist/. Every other owned package loads its source directly, so their edits are live on a window reload.
A
yarn installat thetranquil-clientroot can silently break native modules.better-sqlite3is rebuilt for Electron’s Node ABI, not the system Node’s — a plain root-levelyarn install(e.g. to pick up a newlink:entry, as above) can leave it compiled for the wrong ABI, which surfaces as aNODE_MODULE_VERSIONmismatch crash on next launch (blank window, no obvious cause). Runyarn build(which runselectron-rebuild) after any root-levelyarn installif the app fails to launch afterward.
Verify the sibling symlinks resolved — every owned package must appear, each pointing at ../../tranquil-* (or, for the forked packages, ../../atom-language-csv / ../../atom-pdf-view):
ls -la node_modules | grep -E "tranquil|language-csv|pdf-view" You should see one line per owned package, plus language-csv and pdf-view. A package missing
from this list will not load, no matter what else is wired up — see the box below.
A new owned package needs
yarn install, not just adev/packagessymlink.Adding
"tranquil-x": "link:../tranquil-x"totranquil-client/package.jsondoes nothing on its own: the entry only becomes a realnode_modules/tranquil-xsymlink whenyarn installruns. Pulsar resolvespackageDependenciesfromnode_modules, so until then the package is invisible — no commands in the palette, no menu items, no keybindings, no styles, and no activation error to explain it. It simply is not there.Symlinking into
~/.tranquil/dev/packages/does not substitute for this. That directory is scanned only in dev mode, and it controls whether a package is listed as a Dev Package rather than a Core Package — it is not how the package is found in the first place.So after adding a package, run
yarn installintranquil-clientand confirm the symlink exists. The symptom of skipping it is indistinguishable from “my code never ran”.Then fully restart the app — a window reload is not enough. A window that was already open while the symlink was missing has cached a stub package (name only, no
main), and reloading that window keeps the stub. Adding a New Owned Package has the full checklist and a DevTools one-liner for telling a stub from a healthy package.
Configure tranquil-config
tranquil-config supplies API keys through a generated file that is never committed. The app
won’t start until that file exists. For a first launch you don’t need real keys — write a stub:
cd ~/Documents/Tranquil/Repos/tranquil-config
echo '{"github_token": ""}' > config.json
node generate.js
cd ../tranquil-client When you need real keys later, copy .env.example to .env, fill it in, then run node write_config.js --staging (or --production) followed by node generate.js. Re-run generate.js whenever keys change.
Launch
Start the app. This opens a real Electron GUI:
cd ~/Documents/Tranquil/Repos/tranquil-client
source ~/.nvm/nvm.sh && nvm use
yarn start Launching from an agent shell
yarn start works from an agent/IDE shell as well as a terminal — background it so the agent
keeps control:
cd ~/Documents/Tranquil/Repos/tranquil-client && source ~/.nvm/nvm.sh && nvm use && yarn start The ELECTRON_RUN_AS_NODE gotcha. An agent/IDE shell (e.g. the VSCode extension host Claude
runs inside) exports ELECTRON_RUN_AS_NODE=1. If that leaks into the environment Electron is
spawned with, the binary runs as a plain Node interpreter instead of a GUI app, rejects the
Chromium flags, and dies in ~0.3s with bad option: --no-sandbox. Tranquil’s scripts/dev.js strips the variable from the child env before spawning, so yarn start works from any shell —
no env -u wrapper needed. If you ever see that error, confirm the binary’s mode directly:
electron --version # Node's version (e.g. v20.x) → running as Node
env -u ELECTRON_RUN_AS_NODE electron --version # Electron's version (e.g. v30.x) Prefer a throwaway instance — yarn verify. For agent work that shouldn’t touch your real
window layout or open your actual source tree, yarn verify launches a fully isolated instance
(its own --user-data-dir and ATOM_HOME seeded from ~/.tranquil, opening the dedicated tranquil-test-suite repo instead of tranquil-client) and holds it open:
yarn verify # background this; override the project with: yarn verify <path> Stop either one by stopping the backgrounded launcher — scripts/dev.js forwards SIGINT/ SIGTERM to the Electron child, so the whole app tears down with no orphans.
Verify it’s running
The dev build exposes the Chromium DevTools Protocol on localhost:9222. Confirm a live GUI
without touching the UI:
curl -s http://localhost:9222/json/version A running instance returns the Electron/Chrome versions and a Tranquil/<version> user agent.
To go further — list every renderer/webview target and evaluate JavaScript inside a specific tab
— see Agent Debugging.
Next steps
- Apply the recommended post-launch settings in Preferred Settings.
- Read the Overview and feature tours to learn the workspace.
For deeper reference on how the pieces fit together:
- Multi-Repo Setup — the symlink model,
link:mechanics, and how to add a new owned package. - ppm and Core Packages — how ppm and the bundled Pulsar core packages load in a dev build.
- Agent Debugging — inspect and script a live instance over CDP, and know what a reload actually reloads.