@happyvertical/smrt-cli
Developer CLI with lazy-loaded commands, manifest discovery, and class introspection. Auto-generates CRUD commands for SMRT objects with cli: true in their @smrt() decorator.
Overview
smrt-cli is the developer-facing CLI for the SMRT framework. Commands are
lazy-loaded via dynamic import (~100ms overhead on first use), and the CLI auto-discovers your
project's .smrt/manifest.json plus every installed @happyvertical/smrt-* package. For each registered SMRT object with cli: true in its @smrt() decorator, the CLI generates CRUD commands and
exposes custom methods as additional subcommands.
Installation
pnpm add -D @happyvertical/smrt-cliCommands
Introspection
smrt introspect # Discover SMRT objects in project
smrt objects # List all registered SMRT objects
smrt schema <object> # Show detailed schema for an objectDatabase
smrt db:status # Show migration status (applied, pending, drift)
smrt db:migrate # Apply pending schema changes
smrt db:diff # Compare manifest schema to database
smrt db:rollback # Rollback last migration
smrt db:history # Show migration historyFile-backed migrations are no longer supported, so db:diff's old --generate / --name flags now report "unsupported" — schema changes
are applied directly via db:migrate.
Documentation
smrt docs:agents # Generate AGENTS.md for consumer projects (canonical)
smrt docs:claude # Deprecated alias of docs:agents (Claude-compatible output)docs:agents concatenates the AGENTS.md files of every installed @happyvertical/smrt-* package, prefixed with version tables, into a single
downstream-ready file. docs:claude remains as a deprecated compatibility alias.
Source: packages/cli/src/commands/docs-claude.ts.
Agent knowledge
smrt dev:knowledge-index # Print the deterministic SMRT + SDK knowledge index
smrt dev:knowledge-check # Check deterministic freshness of agent knowledge
smrt dev:knowledge-diff # Show what changed since the last knowledge snapshot
smrt dev:knowledge-review-context # Emit context for a code review
smrt dev:knowledge-architecture-context # Emit architecture contextThese deterministic knowledge commands feed agentic-development workflows. Each dev:knowledge-* command also registers a shorter knowledge:* alias
(knowledge:index, knowledge:check, knowledge:diff, knowledge:review-context, knowledge:architecture-context), so either
form resolves to the same handler.
Dispatch Management
smrt dispatch:list # List dispatch messages
smrt dispatch:process # Process pending dispatches
smrt dispatch:retry # Retry failed dispatches
smrt dispatch:cleanup # Clean up old dispatch recordsPlayground & Code Generation
smrt playground init # Scaffold a developer playground
smrt playground dev # Run the developer playground
smrt playground list # List discovered playground modules
smrt generate-mcp # Generate MCP server from registered objects (alias: mcp)
smrt config:export # Export agent config for SSG
smrt init # Initialize a new SMRT project (alias: setup)Auto-Generated Object Commands
Every SMRT object registered with cli: true gets a generated CRUD command group, plus
custom methods discovered from the manifest:
# Pattern: <object>:<operation>
smrt agent:list # List agents with filtering
smrt agent:get <id> # Get agent by ID or slug
smrt agent:create --name "researcher" # Create new agent (interactive supported)
smrt agent:update <id> --name "updated" # Update existing agent
smrt agent:delete <id> # Delete agent
# Custom methods (auto-discovered from manifests)
smrt agent:research abc123 --query "AI safety"camelCase methods → kebab-case flags
Custom method names stay as written in the command (incorporateFeedback → agent:incorporateFeedback), but their camelCase parameters are exposed as
kebab-case CLI options. The dispatcher converts each --kebab-flag back to the
camelCase parameter name when invoking the method, so a method parameter reviewerProfileId is passed as --reviewer-profile-id. Generated
column filters follow the same rule (a snake_case column becomes a --kebab-case flag).
// Method signature on the SMRT object:
// async incorporateFeedback(args: { reviewerProfileId: string; note?: string })
smrt issue:incorporateFeedback iss-1 \
--reviewer-profile-id editor-1 \
--note "Tightened the lede"CLI ⇄ API coherence check
The CLI invokes object methods over HTTP, so any method a class lists in cli.include needs a matching API route — otherwise the command would be
unreachable at runtime. A build-time lint (validateCliIncludeAgainstApi, run by the
core Vite plugin) walks the manifest and reports every command in cli.include that
is not also exposed via the API.
// Throws at build time: 'create' is in cli.include but not in api.include.
@smrt({
api: { include: ['list', 'get'] },
cli: { include: ['list', 'get', 'create'] },
})
class Widget extends SmrtObject {}
// Fix A — expose it on the API too:
@smrt({
api: { include: ['list', 'get', 'create'] },
cli: { include: ['list', 'get', 'create'] },
})
class Widget extends SmrtObject {}
// Fix B — in-process CLI, acknowledge no HTTP route:
@smrt({
api: { include: [] },
cli: { include: ['list', 'get'], skipApiCheck: true },
})
class Widget extends SmrtObject {}Runtime registration check
Separately from surface coherence, runRuntimeCheck() validates that the build
manifest and the runtime registry agree: it discovers every project and dependency manifest,
verifies each class's canonical qualified name (package:Class), resolves extends references, and confirms own + inherited fields actually hydrate at runtime.
Findings carry codes like qualified-name-mismatch, ambiguous-extends-reference, and runtime-field-hydration; a clean run
emits a single runtime-check-passed finding.
Configuration
@smrt({
cli: true, // Enable all CRUD operations
// OR specify operations:
// cli: { include: ['list', 'get', 'create', 'incorporateFeedback', 'rollback'] }
})
class Issue extends SmrtObject {
title: string = '';
status: 'open' | 'closed' = 'open';
}CLI Configuration (smrt.config.js)
export default {
packages: {
cli: {
entryPoint: './dist/index.js', // default: auto-detected from package.json
database: {
type: 'sqlite', // 'sqlite' | 'postgres'
url: './data.db' // default: ':memory:'
},
format: 'table', // 'table' | 'json' | 'yaml' | 'plain'
}
}
};Entry Point Discovery
- Explicit
entryPointin config (highest) package.jsonexports['.'].importorexports['.']package.jsonmainfield- Fallback:
./dist/index.js
Architecture
- Lazy command loading -- each command is loaded on demand (~100ms first-use overhead)
- Manifest discovery -- auto-finds
.smrt/manifest.jsonand scansnode_modules/@happyvertical/smrt-* - Class loading order --
config.entryPoint→package.json exports['.']→package.json main→./dist/index.js - Object method exposure -- custom methods on SMRT objects auto-become CLI subcommands
Key Files
src/cli-generator.ts-- core dispatcher, lazy command loading, class loadingsrc/commands/-- individual command implementationssrc/loaders/-- class-loader, local-loader, npm-loader, git-loader, template-loadersrc/discovery/manifest-discovery.ts-- manifest auto-discoverysrc/commands/docs-claude.ts-- downstream CLAUDE.md generation (~550 lines)
Gotchas
- Test mode detection: checks
NODE_ENV=test,VITEST=true, and globals likeit/describe-- can collide with other test runners. - External package load failures are silenced: one broken package won't prevent others from loading.
- Schema history nuance:
db:status/db:historyshould distinguish active live drift from superseded failed generated schema repairs. Treating every failed row as a current blocker is a known follow-up.