Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist/
.DS_Store
.env
.env.secrets
*.cast
117 changes: 24 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,40 @@
<em>You never touch the VM. Config lives on your host, git-tracked and reproducible.</em>
</p>

---

**clawctl gives each OpenClaw gateway its own isolated Ubuntu VM** via
[Lima](https://lima-vm.io), provisions it with everything OpenClaw needs, and
manages the full lifecycle from your Mac. You never shell in or piece together
scripts β€” just answer a few questions (or hand it a config file) and the
gateway is running. Config and data are mounted into a project directory on
your host, so they're editable, git-trackable, and safe from VM rebuilds.
<p align="center">
<a href="docs/getting-started.md">Getting Started</a> &middot;
<a href="docs/headless-mode.md">Headless Mode</a> &middot;
<a href="docs/config-reference.md">Config Reference</a> &middot;
<a href="docs/troubleshooting.md">Troubleshooting</a>
</p>

> **Terminology**: A clawctl **instance** is a Lima VM running an OpenClaw
> **gateway**. The gateway hosts one or more **agents** β€” each with its own
> workspace, sessions, and tools. clawctl manages the instance lifecycle;
> OpenClaw manages the agents inside it.
---

## Install

```bash
curl -fsSL https://raw.githubusercontent.com/TimBeyer/clawctl/main/install.sh | bash
```

To update an existing installation, run the same command again.

**Requires** macOS on Apple Silicon (M1/M2/M3/M4) with
[Homebrew](https://brew.sh) installed. Lima is installed automatically if not
already present.
Requires macOS on Apple Silicon (M1–M4) with [Homebrew](https://brew.sh). Lima is installed automatically.

## Quickstart

```bash
# Interactive wizard β€” answers a few questions, does everything else
clawctl create

# Headless β€” config-file-driven, no prompts, great for CI/CD
clawctl create --config config.json
```

## What you get
<p align="center">
<img src="docs/assets/demo.gif" alt="clawctl create wizard">
</p>

In about five minutes, the wizard gives you:

- A running OpenClaw gateway with a dashboard at `http://localhost:18789`
- An isolated Ubuntu 24.04 VM with Node.js, Tailscale, and the 1Password CLI pre-installed
- A project directory on your Mac with git-tracked config and persistent data that survives VM rebuilds

You just answer a few questions. clawctl handles prerequisites, VM creation,
provisioning, and β€” optionally β€” credential setup and OpenClaw onboarding.
For automated setups, pass a JSON config file and skip the prompts β€” see [Headless Mode](docs/headless-mode.md).

## Features

Expand All @@ -70,6 +58,11 @@ provisioning, and β€” optionally β€” credential setup and OpenClaw onboarding.

## Commands

> [!NOTE]
> **Terminology**: A clawctl **instance** is a Lima VM running an OpenClaw
> **gateway**. The gateway hosts one or more **agents**. clawctl manages the
> instance lifecycle; OpenClaw manages the agents inside it.

| Command | Description |
| ------------------------------------------ | ------------------------------------------------- |
| `clawctl create` | Interactive wizard |
Expand All @@ -88,71 +81,14 @@ provisioning, and β€” optionally β€” credential setup and OpenClaw onboarding.
| `clawctl register <name> --project <path>` | Register an existing (pre-registry) instance |
| `clawctl completions <shell>` | Generate shell completion script (bash or zsh) |

All instance commands (`status`, `start`, `stop`, `restart`, `delete`, `shell`,
`openclaw`) accept an optional positional name, a `-i`/`--instance` flag, or
resolve the instance automatically via context. Run `clawctl --help` for
details.

### Instance context

You don't have to type the instance name every time. clawctl resolves the
target instance in this order:

1. `--instance` / `-i` flag β€” `clawctl status -i my-agent`
2. `CLAWCTL_INSTANCE` env var β€” `export CLAWCTL_INSTANCE=my-agent`
3. `.clawctl` file β€” walks up from your current directory (like `.nvmrc`)
4. Global context β€” `~/.config/clawctl/context.json`

Set context with `clawctl use`:

```bash
clawctl use my-agent # write .clawctl in current directory
clawctl use my-agent --global # set global default
clawctl use # show current context and its source
```

### Running openclaw commands from the host
Instance commands accept an optional name, a `-i`/`--instance` flag, or
resolve automatically from context (`CLAWCTL_INSTANCE` env var, `.clawctl`
file, or global default via `clawctl use`).

No need to shell in for routine operations β€” `clawctl openclaw` (or `oc` for
short) runs any `openclaw` subcommand inside the VM:

```bash
clawctl oc doctor # health check
clawctl oc config get gateway.name
clawctl oc daemon status
clawctl oc telegram list
```

For arbitrary commands, use `clawctl shell --`:

```bash
clawctl shell -- whoami
clawctl shell -- systemctl --user status openclaw-gateway
```

### Day-to-day management

clawctl isn't just an installer β€” it's how you manage your gateways after setup too.

```bash
# Set your default instance once
clawctl use my-agent

# See what's running
clawctl list

# Quick health check β€” no need to shell in
clawctl oc doctor

# Restart one that's acting up
clawctl restart

# Spin up a second gateway for a different project
clawctl create

# Tear it down when you're done (keeps your project dir by default)
clawctl delete my-agent
```
> [!TIP]
> No need to shell in for routine operations β€” `clawctl oc` runs any
> `openclaw` subcommand inside the VM: `clawctl oc doctor`,
> `clawctl oc config get gateway.name`, etc.

### Shell completions

Expand All @@ -171,11 +107,6 @@ openclaw subcommand completions (including deep completion like `oc config set
<TAB>`) are cached from the VM and refreshed automatically. See [Shell
Completions](docs/shell-completions.md) for details.

Instances are tracked in `~/.config/clawctl/instances.json` and registered
automatically on create, or manually via `clawctl register`. Run as many
gateways as your hardware allows β€” each gets its own isolated VM, project
directory, and config.

## Documentation

- [Getting Started](docs/getting-started.md) β€” guided walkthrough for first-time users
Expand Down
Binary file added docs/assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions docs/demo-recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Demo Recording

The README GIF (`docs/assets/demo.gif`) is generated from an automated
terminal recording. Re-record it when the wizard UI changes.

## Requirements

```bash
brew install tmux asciinema agg
brew install --cask font-fira-code # for Unicode spinner glyphs
```

## Record and convert

```bash
./scripts/record-demo.sh
agg --font-dir ~/Library/Fonts docs/assets/demo.cast docs/assets/demo.gif
```

The script launches `clawctl create` inside a tmux session, drives the
wizard with scripted keystrokes, and records with asciinema. It runs the
real wizard β€” after recording, the VM cleanup runs normally.

## What it records

The storyboard walks through:

1. Wizard loads with default config
2. Name set to "hal"
3. Provider β†’ Anthropic selected, API key entered
4. Agent Identity β†’ name "Hal", vibe filled in
5. Review screen with validation passing
6. Provisioning starts (runs for ~8 seconds, then recording ends)

## Editing the storyboard

The script is at `scripts/record-demo.sh`. Key helpers:

- `wait_for "text"` β€” wait for text to appear on screen before proceeding
- `type_slow "text"` β€” type with natural speed (50ms per char)
- `down` / `enter` / `esc` β€” send keys with appropriate delay
- `sleep N` β€” visual pacing for the viewer

If navigation changes (fields reordered, sections added), adjust the
`down` counts. The `wait_for` calls are the safety net β€” if navigation
is wrong, the script hangs instead of silently desyncing.

## Files

| File | Purpose |
| ------------------------ | --------------------------------------------------------------- |
| `scripts/record-demo.sh` | Automated recording script |
| `docs/assets/demo.cast` | Raw asciicast recording (gitignored, regenerated by the script) |
| `docs/assets/demo.gif` | Final GIF embedded in README |
4 changes: 2 additions & 2 deletions packages/cli/src/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function runCreateFromConfig(driver: VMDriver, configPath: string):
// /dev/tty unavailable β€” Ink will use process.stdin
}

const useAltScreen = process.stdout.isTTY;
const useAltScreen = process.stdout.isTTY && !process.env.NO_ALT_SCREEN;
if (useAltScreen) {
process.stdout.write("\x1b[?1049h");
}
Expand Down Expand Up @@ -108,7 +108,7 @@ export async function runCreateWizard(driver: VMDriver): Promise<void> {
// Enter alternate screen buffer for a clean canvas. This prevents ghost
// elements from Ink's differential rendering when the output height
// shrinks between phases. On exit, the original terminal is restored.
const useAltScreen = process.stdout.isTTY;
const useAltScreen = process.stdout.isTTY && !process.env.NO_ALT_SCREEN;
if (useAltScreen) {
process.stdout.write("\x1b[?1049h");
}
Expand Down
Loading
Loading