# Quickstart
URL: /docs/cli/quickstart

Start a local OpenSandbox server and fetch your first agent spec in under five minutes.



## Prerequisites [#prerequisites]

* [Docker](https://docs.docker.com/get-docker/) installed and running.
* [Bun](https://bun.sh) installed.

<Steps>
  <Step>
    ### Start OpenSandbox locally [#start-opensandbox-locally]

    Run `alineo init` once per project. It starts an OpenSandbox server in Docker and writes a `alineo.config.json` to your project root.

    ```bash
    bunx alineo-cli init
    ```

    ```
    Checking Docker...
    Starting OpenSandbox in Docker...
    Waiting for OpenSandbox to be ready...
    OpenSandbox running at http://127.0.0.1:8080 — ready.
    ```

    If the server is already running from a previous `init`, `alineo init` prints the URL and exits immediately without starting a second container.
  </Step>

  <Step>
    ### Fetch an agent spec [#fetch-an-agent-spec]

    Point `alineo add` at any URL (or local file path) that returns a valid `AgentSpec` JSON:

    ```bash
    bunx alineo-cli add https://example.com/specs/code-reviewer.json
    ```

    ```
    Agent spec saved: agents/code-reviewer.json
    Load it with: Alineo.start(await Bun.file("agents/code-reviewer.json").json(), { adapter }) from alineo
    ```

    `alineo add` fetches the spec, validates it (`name` and `harness: "pi"` are required), resolves any `registryDependencies` first (fetched recursively, depth-first), and writes it to `<agentsDir>/<name>.json` — `agentsDir` defaults to `./agents`. It does **not** create a sandbox, run setup, or checkpoint anything — that happens later, the first time you `Alineo.start()` the spec.
  </Step>

  <Step>
    ### List your saved specs [#list-your-saved-specs]

    ```bash
    bunx alineo-cli list
    ```

    ```
    NAME                  CLI       DESCRIPTION
    code-reviewer         pi        A Node.js sandbox pre-loaded with TypeScript tooling.
    ```
  </Step>

  <Step>
    ### Run the agent [#run-the-agent]

    `Alineo.start()` (from `alineo`) does the actual sandbox work: on first load it spins up a `node:22` sandbox, installs the Pi CLI and any `setup` steps, then snapshots it. Subsequent starts restore from that setup snapshot, skipping the install.

    ```ts
    import { Alineo, textOnly } from "alineo";
    import { SQLiteAdapter } from "@alineo-labs/sqlite";

    const adapter = new SQLiteAdapter("./.alineo/ledger.db");
    const agent = await Alineo.start(await Bun.file("agents/code-reviewer.json").json(), { adapter });

    for await (const chunk of textOnly(agent.prompt("Review this repo for obvious bugs."))) {
      process.stdout.write(chunk);
    }

    await agent.close();
    ```

    `Alineo.start()` reads `alineo.config.json` itself for the OpenSandbox server URL and other defaults — the same file `alineo init` wrote in step 1. You don't need to construct a `Sandbox` client or pass connection details by hand — the one thing you still construct yourself is the storage adapter (`opts.adapter`), since `alineo` doesn't depend on any specific one.
  </Step>

  <Step>
    ### Remove a spec [#remove-a-spec]

    ```bash
    bunx alineo-cli remove code-reviewer
    ```

    ```
    Removed agent spec 'code-reviewer'
    ```

    This deletes `agents/code-reviewer.json`. It's purely a local file operation — no sandbox or snapshot is touched. If you'd previously run `Alineo.start()` on that spec, its sandbox snapshot (tracked separately, in `agent-snapshots.json` next to your ledger) is unaffected.
  </Step>
</Steps>

## Next steps [#next-steps]

* [Commands reference](/docs/cli/commands) — all flags for each command
* [Registry format](/docs/cli/registry) — publish your own agent spec
* [Running an agent spec](/docs/cli/using-sandboxes) — full details on `Alineo.start()`
* [alineo overview](/docs/agent) — the full Alineo API (prompting, streaming, sessions, snapshotting)
