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.
Path A — ask the assistant to build it
Section titled “Path A — ask the assistant to build it”This is the shortest path and the one with no prerequisites beyond the app itself.
-
Open WAMP and start a new chat. The default agent is the one that can author software; you do not have to pick anything.
-
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 themand a form to add one. -
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.
-
Open the app from the sidebar entry.
-
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.
What you end up with
Section titled “What you end up with”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:
{ "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:
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[].idexport 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.
Path B — write the two files yourself
Section titled “Path B — write the two files yourself”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.
-
Find the folder.
Wampis the product name — a branded build uses its own (Forge writes toForge).Terminal window ~/Library/Application\ Support/Wamp/extensions/dynamic/%APPDATA%\Wamp\extensions\dynamic\Terminal window ~/.config/Wamp/extensions/dynamic/ -
Create a directory named for your extension id —
reading-list. -
Write
ui/index.tsxfirst, thenextension.jsonlast. 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. -
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:
WAMP_DEV_EXTENSIONS="$HOME/src/wamp-extensions" open -a WampPath C — the wamp CLI
Section titled “Path C — the wamp CLI”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:
cd my-app && npm run devIts --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.
Now change something
Section titled “Now change something”Make the loop visible. Open ui/index.tsx, add a button that talks to the host,
and save:
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.
Where to go next
Section titled “Where to go next”- 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.