--- name: dev description: > Local development guide for the Happy monorepo. How to build, install, test, and run the CLI, server, mobile app, and desktop (Tauri) locally. Use when the user types /dev, asks how to "build", "start dev", "install locally", or "run the ___ package". --- # /dev - Local Development Happy is a pnpm monorepo. Everything uses pnpm workspaces — do not use `npm` or `yarn` directly. ## First-time setup ```bash pnpm install # installs deps for every package pnpm --filter happy cli:install # builds happy-cli + links it as the global `happy` binary ``` `cli:install` replaces whatever `happy` is on your PATH (npm-installed or not) with a symlink to `packages/happy-cli/`. Daemon is restarted as part of the script. Uses `~/.happy/` — same as production. To undo: `npm unlink -g happy && npm i -g happy@latest`. ## Packages packages/happy-cli # the `happy` CLI and daemon, published to npm packages/happy-server # Node + Prisma server, deployed via TeamCity packages/happy-app # Expo app: iOS, Android, web, Tauri desktop packages/happy-agent # agent runtime packages/happy-wire # shared Zod schemas + wire types ## happy-cli packages/happy-cli scripts in package.json: typecheck # tsc --noEmit build # rm -rf dist && tsc --noEmit && pkgroll test # build + vitest run cli:install # build + stop daemon + npm link + start daemon prepublishOnly # pnpm test (runs build inside test) postinstall # unpacks difft + rg binaries into tools/unpacked/ Work loop: ```bash pnpm --filter happy cli:install # rebuild + relink + restart daemon happy daemon status # confirm your build is running happy doctor # list all happy processes tail -f ~/.happy/logs/$(ls -t ~/.happy/logs/ | head -1) ``` Run a single test file quickly: ```bash pnpm --filter happy exec vitest run src/path/to/file.test.ts ``` Unit-only (fast, ~1 min): ```bash pnpm --filter happy exec vitest run --project unit ``` Integration tests hit real APIs and are flaky — run on demand, never in the release gate. ### Dev data sandbox (optional) `happy` reads `HAPPY_HOME_DIR` to override `~/.happy/`. To run two versions side-by-side without touching your prod auth: ```bash HAPPY_HOME_DIR=~/.happy-dev happy daemon start HAPPY_HOME_DIR=~/.happy-dev happy auth ``` Point at a local server the same way: ```bash HAPPY_SERVER_URL=http://localhost:3005 happy daemon start ``` ## happy-server ```bash pnpm --filter happy-server standalone:dev # localhost:3005, embedded PGlite, no Docker ``` App auto-reloads on source changes. Point the CLI or the Expo app at it with `HAPPY_SERVER_URL=http://localhost:3005` / `EXPO_PUBLIC_HAPPY_SERVER_URL=...`. ## happy-app (Expo) ```bash pnpm --filter happy-app start # expo start (Metro bundler) pnpm --filter happy-app ios:dev # iOS simulator, development variant pnpm --filter happy-app android:dev pnpm --filter happy-app web # web build, served locally pnpm --filter happy-app tauri:dev # macOS desktop app ``` Variants: development com.slopus.happy.dev # hot reload, internal preview com.slopus.happy.preview # OTA / beta testing production com.ex3ndr.happy # App Store ### Rebuild and reinstall the desktop .app When the user asks to "rebuild the desktop app", "kill the running one and reinstall", or anything in that shape — do all four steps in order, do not stop after building. Variants → product name → build script: production Happy.app pnpm --filter happy-app tauri:build:production preview Happy (preview).app pnpm --filter happy-app tauri:build:preview dev Happy (dev).app pnpm --filter happy-app tauri:build:dev Build output for all variants: packages/happy-app/src-tauri/target/release/bundle/macos/.app If the variant is ambiguous, check what's running with `ps aux | grep "/Applications/.*Happy" | grep -v grep` and match. Production is the default. Steps (substitute `$NAME` with the product name, e.g. `Happy` or `Happy (dev)`): ```bash # 1. build (slow: ~3–10 min, expo web export then cargo release build) pnpm --filter happy-app tauri:build:production # 2. quit the running app gracefully (no-op if not running) osascript -e 'tell application "$NAME" to quit' || true # 3. replace the installed bundle rm -rf "/Applications/$NAME.app" cp -R "packages/happy-app/src-tauri/target/release/bundle/macos/$NAME.app" /Applications/ # 4. relaunch open -a "$NAME" ``` Notes: - Run the build in the background (`run_in_background: true` on Bash) and poll the output file. It prints `Finished \`release\` profile` near the end. - `osascript ... to quit` is graceful — it gives the app a chance to flush state. Only fall back to `pkill -f "/Applications/$NAME.app/Contents/MacOS/app"` if the quit hangs. - Do NOT skip the `rm -rf` before `cp` — `cp -R` over an existing `.app` merges directories and leaves stale files. - If macOS Gatekeeper complains on relaunch, `xattr -dr com.apple.quarantine "/Applications/$NAME.app"` clears it. Local builds are unsigned. ## happy-app-logs (remote log receiver) ```bash pnpm --filter happy-app-logs dev # starts on http://0.0.0.0:8787 ``` Receives POST requests to `/logs` from the mobile app's patched console (see `consoleLogging.ts`). Logs to stdout and `~/.happy/app-logs/.log`. To connect: set the log server URL in the app's dev settings to `http://:8787`. The app's `consoleLogging.ts` sends all console.log/warn/error to this endpoint when configured. Console output must be enabled in the app (dev/preview variants default on, production defaults off, togglable from the dev settings screen). ## Cross-cutting - **Hoisted deps:** pnpm hoists node_modules to the repo root. `packages/*/node_modules/` is mostly empty. Node's resolution walks up, so imports work transparently. - **Workspace deps:** `"@slopus/happy-wire": "workspace:*"` resolves to `packages/happy-wire/` — edits are picked up live. - **`$npm_execpath`:** legacy; happy-cli uses `pnpm` literally. Windows cmd.exe doesn't expand `$VAR`. - **Build before tests:** tests spawn the built CLI binary (for daemon integration), so `pnpm test` runs `build` first. Do not remove. ## Releasing Do not publish by hand. Use `/release` — it handles npm publish, git tags, GitHub releases, and the smoke check. ## Troubleshooting happy: command not found → pnpm --filter happy cli:install daemon won't start → happy daemon stop; rm ~/.happy/daemon.state.json.lock; happy daemon start wrong `happy` version → which happy && ls -la $(which happy) — confirms where it resolves to tools/unpacked missing → pnpm install (postinstall re-extracts) stale deps after branch swap → pnpm install (pnpm is picky about lockfile drift) ## Rules - Never use `npm install` or `yarn install` — only pnpm. - Never add a `dev` / `cli` tsx-based script back to happy-cli. The build step is not optional — daemon spawns the built binary and would desync. - Never bring back `release-it`. Releases go through `/release`. - Never introduce `~/.happy-dev` as a default. It exists as an opt-in via `HAPPY_HOME_DIR`, nothing more.