# trek-plugin-sdk
The SDK for building [TREK](https://github.com/liketrek/TREK) plugins.
The path is four commands: **`create` → `dev` → `status` → `publish`**. Everything
else the CLI can do is a step one of those already does for you.
## Scaffold a plugin
```bash
npx trek-plugin-sdk # no command? a guided menu of everything below
npx trek-plugin-sdk create # interactive wizard (id, type, icon, permissions, egress)
npx trek-plugin-sdk create my-plugin --type widget # or non-interactive
cd my-plugin
```
The wizard asks for the id, type, icon (validated against lucide), permissions,
egress hosts and required addons, and offers to initialize a git repo and install
dependencies for you. In a non-interactive shell (CI, pipes) every command stays
flag-driven with plain output — no prompts, and machine output (`entry` JSON,
`pack --json`, PR URLs) stays on stdout.
What you get **runs and packs immediately** — but it is not publishable yet: the
README is a template and there is no screenshot. That is deliberate. Writing those
is your job, and `status` tells you exactly what is left.
## Develop with a live reload loop
`dev` runs your plugin locally — no full TREK needed. It injects a `ctx` that
enforces exactly the permissions your manifest grants (an ungranted call throws
`PERMISSION_DENIED`, so you catch missing grants), backs `db:own` with a real
SQLite file, serves your routes and your page/widget UI, and reloads on save.
```bash
npx trek-plugin-sdk dev # http://localhost:4317 — dashboard, routes, UI
```
Open **`/preview`** to see a page/widget rendered in a real sandboxed frame with a
theme/accent/appearance toggle (`trek.invoke()` is proxied to your routes). Hit a route
as an unauthenticated request with `?_anon=1`. Drop a `dev-fixtures.json` (trips, users,
config) next to your manifest to feed `ctx.trips` / `ctx.users`.
## Know where you are
```bash
npx trek-plugin-sdk status # never fails — it's a map, not a gate
```
`status` runs every check the TREK-Plugins registry enforces that can be answered
without a network — which is nearly all of them — and prints the whole journey as a
checklist grouped by stage (Manifest, Code, Docs, Release, Repo): what passes, what
does not, and the **one** command to run next. Run it whenever you are not sure what
to do; it deliberately never exits non-zero.
`validate` is the same checks with an exit code. It is the form for scripts and CI;
`status` is the form for humans. A plugin that passes `validate` will pass every
registry gate that does not require the tag and the release to exist — those four
run in `preflight`, which `publish` does for you.
## Build a native UI (page / widget)
The UI is a sandboxed, opaque-origin iframe that can't load TREK's stylesheet — so the
SDK ships it. Put **one line** in your `client/index.html` `
`:
```html
```
`dev` and `pack` expand it into the inlined **design kit**: token-driven styles that
follow the host's theme and accent (glass, cards, `.trek-btn`, `.trek-input`,
`.trek-chip`, `.trek-row`, hover), plus a `window.trek` bridge:
```js
trek.onContext((ctx) => { /* ctx.theme, ctx.tokens, ctx.appearance, ctx.user, ctx.tripId */ })
const data = await trek.invoke('/status') // calls your own route, host-proxied
trek.notify('success', 'Saved')
```
### Keep UI state for this browser tab
The opaque frame cannot use browser storage directly. The bridge provides
host-managed, JSON-only session state instead. It survives an iframe remount and
page reload in the same browser tab, but is not durable data. Do not use it to
store secrets.
```js
// Default: this user + this plugin + this browser tab.
await trek.session.set('dismissed-onboarding', true)
const dismissed = await trek.session.get('dismissed-onboarding')
// Explicitly partition state by the trip in view (fails without a trip context).
await trek.session.set('filters', ['flight', 'hotel'], { scope: 'trip' })
```
`get()` returns `undefined` for a missing key. `set`, `get`, `remove`, and
`clear` accept `{ scope: 'plugin' | 'trip' }`; `plugin` is the default. Use your
plugin's own logical key (for example `reservation:88:expanded`) for any finer
partitioning. Each plugin or individual trip scope is limited to 32 keys,
64 characters per key, and 1 KiB per JSON value.
The kit applies the theme, mirrors the appearance flags (reduced-motion,
no-transparency) and auto-reports your height. It also upgrades any native
`