A static demo of s-m-r-t. Add recipes (Customers, Sales, Purchases, ...) or whole packages and an app mock-up assembles around them: a shell with navigation, a list view and create/edit form per model with sample data, and a plain-language Help page per recipe that follows the app's options, with a collapsed "Connect other tools" section listing the REST routes, MCP tools and CLI commands s-m-r-t generates from each model's decorator.
It is a purely static site (SvelteKit + adapter-static). Nothing is
installed or stored on a server; sample data lives in memory and resets on
reload. The layout comes from the canonical starter,
smrt-start, minus everything
server-side.
Requirements: Node.js 26+ and pnpm 11.25 (packageManager pins it).
pnpm install
pnpm dev # http://localhost:5173Static build, servable from any static host:
pnpm build # writes ./build
pnpm preview # or: npx sirv-cli buildHosting under a sub-path (for example GitHub Pages)? Build with
BASE_PATH=/smrt-planner pnpm build.
- On the Planner page, add recipes: small units like Customers, Vendors, Sales (needs Customers) and Purchases (needs Vendors). Adding one also adds what it requires. The whole package catalog is still there under "All packages".
- A recipe opens an options form built from its models' own parameters:
switch fields on or off (required ones stay on), set label, help text,
default value and order, and narrow REST, MCP and CLI exposure. Saving writes
field policies in the
@happyvertical/smrt-fieldsshape, held in memory for now. - The left navigation grows a group per recipe with its entries (Customers, Sales Orders, ...). Open one to browse sample rows and create, edit or delete them. The generated views and each recipe's Help page (overview, tasks and a field glossary) follow the options: hide a field and its glossary entry and any task step about it disappear. Money fields are stored as integer minor units (cents).
- Recipes and options live in the URL (
?r=commerce.sales,...&o=...); copy it to share the mock-up.
Short on time? The Cookbooks tab (where a first visit lands) offers ready-made starting points for a bakery, a mechanic, a welder and a yoga studio: pick one, read what it adds (recipes, menu, default payment terms, currency and tax), and use it to replace your recipes and menu. Your records stay.
The Assistant runs a small model on your device. The chat icon at the end of
the header opens it once a model is ready; before that it opens the AI models
page (/ai/), so a first click sets inference up instead of showing an empty
chat. Three icons in the left sidebar footer show Think (the language
model), Hear (voice typing) and Speak (read replies aloud); each opens
the same page, where you choose and download them. A first visit with nothing built starts there; "I don't
need AI, let's just build" skips it for good. Live s-m-r-t objects are tracked in #4.
Generated record forms use the Planner's own responsive, open form layout. Generated record lists let you sort any data column by selecting its heading; select it again to reverse the order. Sorting changes only the current view. The left sidebar starts open, with inference controls above the user menu in its footer. The Theme tab offers palette previews, large appearance choices, a brand colour wheel and font selection. Advanced Styles opens a dedicated CSS editor with syntax highlighting. CSS applies to the Planner as it changes and travels with the cookbook when saved, exported or imported.
The primary package registry is npm.happyvertical.com. Configure the scope
once, then install the planner:
pnpm config set @happyvertical:registry https://npm.happyvertical.com/
pnpm add @happyvertical/smrt-plannerThe same version is mirrored to npmjs on a best-effort basis. Planner mounts
the cookbook-driven app shell and its customization controls. A host can drive
it with the typed command set:
<script lang="ts">
import { Planner, createPlannerController } from '@happyvertical/smrt-planner';
const controller = createPlannerController();
</script>
<Planner {controller} basePath="/planner" />Planner props, all optional: controller, basePath (where its pages are
served from), persistence ('self' reads and saves the cookbook in this
browser, 'host' leaves both to you), themeControls, defaultTitle (the shell title when no cookbook name or
library cookbook applies; defaults to 'Planner'), tabs, onready, and:
The mounted planner is the framework's cookbook-driven app shell with its
customizable overviews switched on: the pencil at the start of the header
edits the menu and every section page in place (add, move, resize, configure
and remove widgets), and each edit lands in the cookbook's layout and
overviews, so it is saved, exported and imported with the rest of the plan.
The metric, records and chart widgets read the in-browser sample data;
the chart groups the seeded rows itself (by a field, or a date field bucketed
over the widget's period) as the stand-in for the runtime report a generated
app's chart loader would compile. Its pages are fragment routes (#/s/...,
#/m/..., #/packages/...), so the mount needs nothing from the host's
router and every in-planner link stays inside it.
backLink: { href, label }renders a link out of the planner at the start of its header, beside the brand (a docs site's "Back to documentation").inferencegives the planner's assistant a model and switches the assistant on: the chat icon at the end of the header opens the chat dock once a model is ready, and the AI models page (#/ai/) before, where a visitor downloads a browser model or connects their own endpoint. Pass the same object asplanner.config.json'sinference({ mode, alternatives?, credentialPersistence?, host?, byo? }, seedocs/inference-host.md) or{ chat }with a model of your own. For a public site where visitors bring their own endpoint:inference={{ mode: 'byo', alternatives: ['browser'], credentialPersistence: 'memory' }}. Withoutinferencethe planner has no assistant and no chat icon.
<Planner
basePath="/planner"
backLink={{ href: '/', label: 'Back to documentation' }}
inference={{ mode: 'byo', credentialPersistence: 'memory' }}
/>Hosts that already own an AppShell can mount PlannerControls inside it.
The controls are the reusable part; Planner remains the complete shell.
Use the exported shell defaults to start navigation open, with inference
controls above the user menu in its footer. The bottom panel starts hidden.
<script lang="ts">
import { AppShell } from '@happyvertical/smrt-svelte/app';
import {
PLANNER_SHELL_EDGE_TOGGLES,
PLANNER_SHELL_PANEL_DEFAULTS,
PlannerControls,
} from '@happyvertical/smrt-planner';
</script>
<AppShell
edgeToggles={PLANNER_SHELL_EDGE_TOGGLES}
config={PLANNER_SHELL_PANEL_DEFAULTS}
layoutEditing
>
<PlannerControls layout="shell" persistence="host" />
</AppShell>import { commandTools, createPlannerController } from '@happyvertical/smrt-planner/commands';
const controller = createPlannerController();
const result = controller.run({
name: 'add_recipes',
input: { ids: ['commerce.sales'] },
});
// { ok: true, snapshot, receipt } | { ok: false, error: { code, message } }
controller.subscribe((snapshot) => render(snapshot));commandTools is every command as { name, description, inputSchema }: pass it
straight to an LLM or an MCP server as tool definitions. controller.snapshot()
is a compact read-only plan (a few KB) fit for a model prompt. Commands:
add_recipes, remove_recipes, add_features, remove_features,
add_cookbook, remove_cookbook, apply_cookbook, import_cookbook,
set_settings, set_policy, set_theme, reset_theme, rename_section,
rename_item, hide, show, focus, export_cookbook, undo.
@happyvertical/smrt-planner and @happyvertical/smrt-planner/commands are Svelte source (runes) and JSON,
so consume them through Vite or another Svelte-compiling bundler. A plain Node
server uses @happyvertical/smrt-planner/core instead (command schemas, the assistant prompt,
reply validation, the library catalog) and can serve the prebuilt app from
@happyvertical/smrt-planner/app; see docs/inference-host.md.
src/lib/catalog/catalog.json is committed and is the only thing the app
reads; pnpm build needs no network. Regenerate it with:
pnpm catalog:generateThe script (scripts/generate-catalog.ts):
- Lists every
@happyvertical/smrt-*package through the registry's search (the registry.npmrcmaps the@happyverticalscope to; override withSMRT_PLANNER_REGISTRY). - Downloads each package's latest published tarball, reads the
./manifest.jsonexport and thesmrt-knowledge.jsonbeside it. Packages without a manifest export (cli, svelte, ui, ...) or without models are skipped. - Drops the infrastructure packages in
src/lib/catalog/generate/exclusions.ts(core, tenancy, jobs, prompts, ...); that list is the only hand-maintained part, and each entry says why. A listed package that declares recipes stays (smrt-chatbrings the assistant,smrt-fieldsform customization): a recipe is a feature. - Writes, per package: description, models, each model's fields and types, the
generated REST routes, MCP tools and CLI commands (from the knowledge
artifact's
surfaces), public methods and which are AI-callable (have an MCP tool), the recipes it declares with their surfaces, providers (secret names only), runtime, demo seed and browser-demo mode (demo, plus the package'sbrowsercapability; smrt#3708/#3709), and dependencies (the manifest'ssmrtDependenciesplus models' foreign keys and cross-package references, limited to catalog packages).
Output is sorted and has no timestamps, so a run against the same published
versions is byte-identical. It tracks each package's latest dist-tag, so
rerunning later may change the file; commit the result when it does.
| Script | Does |
|---|---|
pnpm dev |
Vite dev server |
pnpm build |
Static build into build/ |
pnpm catalog:generate |
Regenerate the committed catalog |
pnpm typecheck |
svelte-kit sync, tsc, svelte-check |
pnpm test |
Vitest |
pnpm test:package -- <tarball> |
Install and exercise an already packed tarball as an isolated consumer |
pnpm test:release-workflow |
Check the protected publication workflow contract |
pnpm lint |
Biome |
pnpm package |
svelte-package into dist/, then publint |
Maintainers publish only through the protected GitHub Actions workflow. See the release procedure; never publish this package from a workstation.
MIT © Happy Vertical Corporation