# Happy

Code on the go — control AI coding agents from your phone, browser, or terminal.

Free. Open source. Code anywhere.

## Installation

```bash
npm install -g happy
```

Installing or updating Happy also installs the two companion CLIs its credential
runtime drives: `codex-multi-auth` goes into npm's global prefix next to `happy`
itself, and `claude-swap` is installed with uv, which puts the `claude-swap` and
`cswap` commands in uv's tool bin directory — Happy looks for them there even
when that directory is not on your `PATH`. Both are installed at the exact
versions Happy requires rather than the newest, because Happy checks those
versions and reinstalls its own pinned copy if it finds anything else. Set
`HAPPY_SKIP_COMPANION_TOOLS=1` to skip this step; Happy installs either one on
demand when it needs it. Neither can fail the Happy install — a missing
`npm`/`uv` or a failed install is only a warning.

> Migrated from the `happy-coder` package. Thanks to [@franciscop](https://github.com/franciscop) for donating the `happy` package name!

## Usage

### Claude Code (default)

```bash
happy
# or
happy claude
```

This will:
1. Start a Claude Code session
2. Display a QR code to connect from your mobile device or browser
3. Allow real-time session control — all communication is end-to-end encrypted
4. Start new sessions directly from your phone or web while your computer is online

### More agents

```
happy codex
happy gemini
happy openclaw

# or any ACP-compatible CLI
happy acp opencode
happy acp -- custom-agent --flag
```

## Daemon

The daemon is a background service that stays running on your machine. It lets you spawn and manage coding sessions remotely — from your phone or the web app — without needing an open terminal.

```bash
happy daemon start
happy daemon stop
happy daemon status
happy daemon list
```

The daemon starts automatically when you run `happy`, so you usually don't need to manage it manually.

### Keeping the daemon running across reboots

If you want the daemon to come back automatically after a reboot — without opening a `happy` session first — start it from your shell profile so it inherits your normal user session context (PATH, keychain access, OAuth credentials):

```bash
# ~/.zshrc or ~/.bashrc
if [[ -o interactive ]] && [[ -z "$HAPPY_DAEMON_CHECKED" ]]; then
    export HAPPY_DAEMON_CHECKED=1
    () {
        local state=$HOME/.happy/daemon.state.json
        local pid=$(grep -oE '"pid"[[:space:]]*:[[:space:]]*[0-9]+' "$state" 2>/dev/null | grep -oE '[0-9]+')
        if [[ -z "$pid" ]] || ! kill -0 "$pid" 2>/dev/null; then
            happy daemon start >/dev/null 2>&1
        fi
    } &!
fi
```

The first interactive shell after a reboot triggers the start; subsequent shells short-circuit because the daemon is already running.

> **macOS users:** prefer this shell-init approach over a `launchd` LaunchAgent. A LaunchAgent runs in an agent domain that is **detached from your GUI/Aqua login session**, which means the bundled `claude-agent-sdk` cannot reach the macOS keychain and silently fails authentication ("Failed to authenticate. API Error: 401 terminated", `duration_api_ms: 0`). If you must use launchd, your wrapper has to read the OAuth access token from `~/.claude/.credentials.json` and export it as `CLAUDE_CODE_OAUTH_TOKEN` before exec'ing the daemon — and you'll need to handle token rotation yourself.

## Authentication

```bash
happy auth login
happy auth logout
```

Happy uses cryptographic key pairs for authentication — your private key stays on your machine. All session data is end-to-end encrypted before leaving your device.

To connect third-party agent APIs:

```bash
happy connect gemini
happy connect claude
happy connect codex
happy connect status
```

## Commands

| Command | Description |
|---------|-------------|
| `happy` | Start Claude Code session (default) |
| `happy codex` | Start Codex mode |
| `happy gemini` | Start Gemini CLI session |
| `happy openclaw` | Start OpenClaw session |
| `happy acp` | Start any ACP-compatible agent |
| `happy resume <id>` | Resume a previous session |
| `happy agent <verb>` | Let an AI agent inspect and control owned child sessions |
| `happy notify` | Send push notification to your devices |
| `happy doctor` | Diagnostics & troubleshooting |

### Agent orchestration

`@buzzni/happy-cli` includes the exact compatible `@buzzni/saycode-cli`
runtime, so sessions started by Happy can use `happy agent` without a separate
Saycode CLI install. This command is intended for AI agents running inside a
Happy/Saycode session; it fails closed outside that session environment.

The supported verbs include capability discovery and owned-child lifecycle
operations such as `whoami`, `ls`, `spawn`, `prompt`, `steer`, `stop`, `read`,
and `wait`. Run `happy agent --help` for the current machine-readable contract.
Happy deliberately does not install a top-level `saycode` binary, so an
independently installed Saycode CLI remains untouched.

---

## Advanced

### Environment Variables

| Variable | Description |
|----------|-------------|
| `HAPPY_SERVER_URL` | Custom server URL (default: `https://api.cluster-fluster.com`) |
| `HAPPY_WEBAPP_URL` | Custom web app URL (default: `https://app.happy.engineering`) |
| `HAPPY_HOME_DIR` | Custom home directory for Happy data (default: `~/.happy`) |
| `HAPPY_DISABLE_CAFFEINATE` | Disable macOS sleep prevention |
| `HAPPY_EXPERIMENTAL` | Enable experimental features |

### Sandbox (experimental)

Happy can run agents inside an OS-level sandbox to restrict file system and network access.

```bash
happy sandbox configure
happy sandbox status
happy sandbox disable
```

### Building from source

```bash
git clone https://github.com/slopus/happy
cd happy-cli
yarn install
yarn workspace happy cli --help
```

## Requirements

- Node.js >= 20.0.0
- For Claude: `claude` CLI installed & logged in
- For Codex: `codex` CLI installed & logged in
- For Gemini: `npm install -g @google/gemini-cli` + `happy connect gemini`

## Troubleshooting

### `Cannot find package '@slopus/happy-wire'`

Symptom:

```text
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@slopus/happy-wire'
```

Cause: the source package uses the workspace dependency
`@slopus/happy-wire: file:../happy-wire`. Installing or linking the source
directory directly can leave the global package without that runtime
dependency.

Fix from this checkout:

```bash
# from the aplus-dev-studio repo root
cd vendor/happy/packages/happy-cli
corepack pnpm run cli:install
```

Avoid `npm install -g .` and `npm link` for daemon runtime installs. For publish
work, prepare a clean package first and guard it:

```bash
tmp=$(mktemp -d)
corepack pnpm run build
node scripts/prepare-publish-package.cjs --out "$tmp/package"
node scripts/guard-publish-artifact.cjs "$tmp/package" --install-smoke
```

### Remote terminal — `posix_spawnp failed.` on macOS

Symptom (web-ui "터미널" tab 또는 daemon log):
```
[REMOTE-TERMINAL] terminal-open-fwd spawn failed: posix_spawnp failed.
```

Cause: npm extracts `node-pty/prebuilds/darwin-{arm64,x64}/spawn-helper`
without the executable bit (`0o644`), so macOS `posix_spawnp` refuses
to `execve()` it. Linux uses `forkpty + execvp` and is unaffected.

**Auto-fix**: shipped in `@namsangboy/happy-cli@1.1.4-aplus.9+` —
postinstall flips the bit on macOS. Just upgrade:

```bash
npm i -g @buzzni/happy-cli@latest
happy daemon stop && happy daemon start
```

**Manual fix (older versions)**:

```bash
chmod +x $(npm root -g)/@buzzni/happy-cli/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper
chmod +x $(npm root -g)/@buzzni/happy-cli/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper
happy daemon stop && happy daemon start
```

**Or rebuild from source** (also works, but requires Xcode CLT):

```bash
xcode-select --install   # if not already
npm i -g @buzzni/happy-cli@latest --build-from-source
```

### Other

- `happy doctor` runs platform diagnostics and is the first thing to try
  before opening an issue.
- daemon logs live in `~/.happy/logs/*-daemon.log`. Grep for
  `[REMOTE-TERMINAL]` to inspect terminal-relay traffic without leaking
  payload bodies (only metadata is logged — `bytesIn`, `bytesOut`,
  `exitCode`, `signal`, `durationMs`).

## License

MIT
