Skip to content

Quickstart

By the end of this page you have an app in your own WAMP window: an entry in the sidebar, a page of your code behind it, and a loop where saving a file rebuilds and reloads it without restarting anything.

You need a WAMP desktop build, installed and signed in — model calls go through your account, so the assistant needs it. You do not need Node, a bundler, or any other toolchain: the host builds extensions with its own embedded esbuild.

This is the shortest path and the one with no prerequisites beyond the app itself.

  1. Open WAMP and start a new chat. The default agent is the one that can author software; you do not have to pick anything.

  2. Describe the app in one message. Be concrete about the surface and the data:

    Build me an app called Reading List that stores books I want to read —
    title, author, a status of want/reading/done — with a page that lists them
    and a form to add one.
  3. Watch what happens. The agent scaffolds the extension (a manifest plus a page), writes the code, and the host takes it from there: it typechecks the source against the platform’s own types, bundles it, and checks that the page really mounts and paints. A sidebar entry appears, with a toast offering to open it. The agent confirms the build landed before it finishes the turn — it is not allowed to end a turn leaving an app that does not build.

  4. Open the app from the sidebar entry.

  5. Ask for a change: “make the header show how many books are unread”. The file is rewritten, the save triggers a rebuild, and the open window updates. There is no build command in this loop, for you or for the agent.

Whichever path you take, the artifact is the same and it is small:

  • Directoryreading-list/
    • extension.json the manifest — identity, what it contributes, what it may touch
    • package.json identity and version only
    • Directoryui/
      • index.tsx the page component, exported under its page id

No build.mjs, no tsconfig.json, no node_modules. An extension that ships source and no build script is built by the host, which is what keeps it editable later with nothing installed.

The manifest is the whole declaration:

extension.json
{
"name": "Reading List",
"version": "1.0.0",
"description": "Books to read.",
"icon": "BookOpen",
"compat": { "pluginApi": "^1.0.0" },
"contributes": {
"pages": [
{ "id": "reading-list", "title": "Reading List", "icon": "BookOpen", "context": "both" }
]
}
}
Key Why it is there
name, version Required. version is strict MAJOR.MINOR.PATCH — no pre-release tags.
icon A lucide icon name in PascalCase.
compat.pluginApi The plugin-API range you target. If it does not match the host, activation refuses with a clear message instead of crashing later.
contributes.pages[] The sidebar surface. context is "global", "project", or "both". Add "presentation": "app" to take the window over instead of docking.

The extension’s id is its directory name — kebab-case, starting with a letter. There is no id field in the manifest.

The page component is exported under that page id. This is the smallest file that renders:

ui/index.tsx
function ReadingListPage() {
return (
<div className="h-full p-6 bg-background text-foreground">
<h1 className="text-lg font-semibold">Reading List</h1>
</div>
);
}
// Keys must match contributes.pages[].id
export const views = {
'reading-list': ReadingListPage,
};

react, @wamp/ui, @wamp/plugin-api, lucide-react, and zustand are provided by the host — import them, never install them. Colors come from theme tokens (bg-background, text-muted-foreground, …) so the page follows the user’s theme.

The host loads development extensions from a folder it watches. Put a directory there and it is picked up live: no CLI, no install step, no restart.

  1. Find the folder. Wamp is the product name — a branded build uses its own (Forge writes to Forge).

    Terminal window
    ~/Library/Application\ Support/Wamp/extensions/dynamic/
  2. Create a directory named for your extension id — reading-list.

  3. Write ui/index.tsx first, then extension.json last. The watcher activates the extension the moment the manifest appears, so writing it last means the first activation sees a complete extension. Both files are above.

  4. The running app registers it, builds it, and offers to open it. If you write the manifest before the page, save the page afterwards and it rebuilds.

To keep your source somewhere else — a git repository, your projects folder — set WAMP_DEV_EXTENSIONS to a directory that contains extension directories. The linking happens at startup, so quit the app first; every subdirectory with an extension.json is then symlinked into the folder above and gets the same hot-reload behavior:

Terminal window
WAMP_DEV_EXTENSIONS="$HOME/src/wamp-extensions" open -a Wamp

The SDK ships a scaffolder, wamp init <name>, with a minimal template (a page) and a full template (typed storage, a cron job, an AI session, and a contributed tool).

A CLI-scaffolded extension differs from the two paths above in one way that matters: it ships its own build.mjs, so it owns its build and the host will not build it for you. Run the watcher yourself, and the host reloads the app each time a new bundle lands:

Terminal window
cd my-app && npm run dev

Its --install flag symlinks into ~/.wamp/extensions/dynamic/, which is not the folder the desktop host loads from — use the path in Path B, or WAMP_DEV_EXTENSIONS, and the extension is picked up.

Make the loop visible. Open ui/index.tsx, add a button that talks to the host, and save:

ui/index.tsx
import { Button } from '@wamp/ui';
import { pluginAPI } from '@wamp/plugin-api';
function ReadingListPage() {
return (
<div className="h-full p-6 bg-background text-foreground">
<h1 className="text-lg font-semibold">Reading List</h1>
<Button className="mt-4" onClick={() => pluginAPI.notify.success('Still here')}>
Say hello
</Button>
</div>
);
}
export const views = {
'reading-list': ReadingListPage,
};

The open window picks up the new code without a restart, and the button raises a host toast. That is the whole authoring loop: edit source, save, see it.

If the page fails to render after a save, the most common cause is importing a symbol @wamp/ui does not export. The build names it exactly, and the interface kit has the list of what exists.

  • Core concepts — the vocabulary, each term building on the last. Read this before your second file.
  • The manifest — every key, and what declaring it does.
  • Typed data — storage with a schema, shared by your page and your background code, which is where most apps go next.
  • Contributing tools — let the assistant call your app’s own functions.
  • Choosing a path — how this reaches other people.