You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TypeScript framework for vertical AI agents. Define business logic as TypeScript classes with @smrt() — get REST APIs, CLI tools, MCP servers, and AI operations (is()/do()) automatically.
Monorepo Packages
All @happyvertical/smrt-* packages are version-locked via changesets. pnpm workspace.
Foundation
Package
Purpose
core
ORM (SmrtObject/SmrtCollection), @smrt() decorator, code generators (REST/CLI/MCP), DispatchBus, STI
Svelte 5: Provider, browser AI (STT/TTS/LLM) with warm cache, theme system
smrt-dev-mcp
Tier 2 dev MCP: generate-smrt-class, introspect-project
gnode
Federation library — stubs only, not implemented
template-sveltekit
Base SvelteKit scaffold with SMRT integration
template-site-static-json
Community news site scaffold with Praeco/Caelus
Commands
pnpm install && npm run build # Setup (~8s first build, ~80ms cached via turborepo)
npm run dev # Watch mode
npm test# Vitest — smrtVitestPlugin() required in config
npm run typecheck # TypeScript checking
npm run lint # Biome
npm run format # Biome
Conventions
0 vs 0.0: count: number = 0 → INTEGER. price: number = 0.0 → DECIMAL
Never override toJSON() — use transformJSON() (toJSON handles STI + meta fields)
Cross-package FKs: plain string IDs, not @foreignKey() (avoids circular deps)
System tables: prefixed _smrt_ (jobs, dispatch, schedules, migrations)
Conflict columns: set conflictColumns in @smrt() for junction/upsert tables
Asset ownership joins: base/domain-owned asset relationships belong on noun join tables like content_assets, profile_assets, event_assets, place_assets, and product_assets; use asset_associations only for generic/provenance links
STI discriminator: qualified names — @happyvertical/smrt-content:Article
Tenant scoping: most domain models use @TenantScoped({ mode: 'optional' }) + nullable tenantId
JSON fields: store as string, provide getX()/setX() helpers with graceful parse error handling
No private reach-ins: do not cast into underscored internals like _db, _tableName, or registry-private state from outside the owning class. If a public API is missing, add one upstream instead of reaching through a private implementation detail.
Changesets: auto-generated on merge to main. Don't run npx changeset manually
smrt docs:Codex generates .Codex/smrt-framework.md for consumer projects by concatenating installed package AGENTS.md files with version tables. Code: packages/cli/src/commands/docs-Codex.ts.
Gotchas
Vitest plugin required: without smrtVitestPlugin() in vitest.config.ts → "No field metadata" errors
Vite decorators: needs esbuild.tsconfigRaw with experimentalDecorators: true, emitDecoratorMetadata: true
Manifest is build-time: generated once at vitest startup — restart after adding new @smrt() classes
ObjectRegistry on globalThis: singleton via globalThis.__smrtRegistry* — survives HMR
No runtime schema creation: application tables must be prepared explicitly via migrations/tooling; runtime only verifies and fails clearly
TypeScript & Svelte 5 Performance: Avoid using inline intersected generic types (like Asset & { id: string }) in component $props(). It causes infinite-loop-like recursion during type evaluation. Export an explicit interface (e.g., interface PersistedAsset extends Asset { id: string }) instead.