# Quickstart

Terminal window

```
# 1. Add the skill to your coding agent (Claude Code, Cursor, Codex, Gemini, Copilot, Windsurf).
npx skills add aerolalit/paneui --skill app


# 2. Register this agent with the relay. Writes ~/.config/homespun/config.json.
homespun agent register --name "my-agent"


# 3. A human generates a one-time claim code from the Homespun web UI (sign in,
#    then "Connect your agent"), then you claim it. This binds the agent to
#    that human's account. Replace cc_example with the real code.
homespun agent claim cc_example


# 4. Deploy an app. See "The two files" below for what ./my-app needs to contain.
homespun deploy ./my-app


# 5. Seed it with a first row of real data (replace app_abc123 with the
#    app_id your deploy command just printed).
homespun data app_abc123 todos upsert --data '{"text":"buy milk"}'
```

That’s the whole loop: an agent identity, a human owner, a deployed app, and a row of shared data the human can already see.

## 1. Install the skill

[Section titled “1. Install the skill”](#1-install-the-skill)

Terminal window

```
npx skills add aerolalit/paneui --skill app
```

This fetches the current [`SKILL.md`](https://homespun.dev/skills/homespun/SKILL.md) into your coding agent, which is the full reference for the manifest and `window.homespun` model. It keeps itself current: the skill compares its own version against the relay (`homespun skill version --plain`) and refreshes itself with `homespun skill show` when it’s behind, so what your agent follows always matches the relay it’s talking to.

## 2. Register your agent

[Section titled “2. Register your agent”](#2-register-your-agent)

Terminal window

```
homespun agent register --name "my-agent"
```

This provisions an API key and saves it, along with the relay URL, to `${XDG_CONFIG_HOME:-~/.config}/homespun/config.json` under a named profile. Every other command reads from there, so you don’t need to pass the key around by hand. Self-hosting your own relay? Add `--url <your relay origin>`.

## 3. Claim the agent for a human

[Section titled “3. Claim the agent for a human”](#3-claim-the-agent-for-a-human)

An agent needs an owning human before it can deploy anything a person will actually see. On the Homespun web UI, a signed-in human generates a one-time claim code (it expires after a few minutes), then you consume it:

Terminal window

```
homespun agent claim cc_example
```

This is one-way: there’s no unclaim yet. From this point on, anything this agent deploys shows up on that human’s Home.

## 4. Deploy an app

[Section titled “4. Deploy an app”](#4-deploy-an-app)

Homespun expects exactly two files, at fixed names, in one directory:

```
my-app/
├── index.html
└── manifest.json
```

`manifest.json` declares the app’s identity and its data model, a small JSON Schema document with one namespaced extension key. A minimal one, for a shared todo list:

```
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "Todo": {
      "type": "object",
      "properties": {
        "text": { "type": "string" },
        "done": { "type": "boolean" }
      },
      "required": ["text"]
    }
  },
  "x-homespun-manifest": {
    "app": {
      "name": "Todos",
      "description": "A shared todo list",
      "icon": "✅"
    },
    "collections": {
      "todos": {
        "schema": { "$ref": "#/$defs/Todo" },
        "write": ["agent", "owner", "member"],
        "delete": ["agent", "owner", "member"]
      }
    }
  }
}
```

`index.html` is a normal HTML page. Homespun injects the SDK script for you; read [SDK reference](/agents/sdk-reference/) before writing anything that touches `window.homespun`, the script-ordering rule there is easy to trip over. A minimal page for the manifest above:

```
<!doctype html>
<html>
  <head>
    <title>Todos</title>
  </head>
  <body>
    <ul id="list"></ul>
    <script>
      document.addEventListener("DOMContentLoaded", async () => {
        await homespun.ready;
        const render = () => {
          document.getElementById("list").innerHTML = homespun.collections
            .snapshot("todos")
            .map((row) => `<li>${row.data.text}</li>`)
            .join("");
        };
        render();
        homespun.collections.on("todos", render);
      });
    </script>
  </body>
</html>
```

Then deploy it:

Terminal window

```
homespun deploy ./my-app
```

This prints `{ app_id, slug, url, version, visibility, created, ... }`. `url` is the address to share. If you omit `--visibility`, see [Visibility, explained](/people/visibility-explained/) for what a new app gets by default today. To redeploy after changing files, pass the same `--app <app_id>` back in: `homespun deploy ./my-app --app app_abc123`.

## 5. Seed it with data

[Section titled “5. Seed it with data”](#5-seed-it-with-data)

Terminal window

```
homespun data app_abc123 todos upsert --data '{"text":"buy milk"}'
```

`upsert` is the one create-shaped verb for rows: omit `--key` to add a new row with a server-generated key, or pass `--key` to ensure a row exists at that key without erroring on a collision. From here, both you and the app’s owner are reading and writing the same `todos` collection, whichever side changes it, the other side sees it live.

## Where to go next

[Section titled “Where to go next”](#where-to-go-next)

* [CLI reference](/agents/cli-reference/) for every command this CLI supports.
* [Manifest reference](/agents/manifest-reference/) for the full permissions model (`write`/`delete`/`read`, roles, worked examples).
* [SDK reference](/agents/sdk-reference/) for the complete `window.homespun` surface.
