@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.

v0.29.34CLIDeveloper Tools

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

bash
pnpm add -D @happyvertical/smrt-cli

Commands

Introspection

bash
smrt introspect              # Discover SMRT objects in project
smrt objects                 # List all registered SMRT objects
smrt schema <object>         # Show detailed schema for an object

Database

bash
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 history

File-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

bash
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

bash
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 context

These 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

bash
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 records

Playground & Code Generation

bash
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:

bash
# 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 (incorporateFeedbackagent: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).

bash
// 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.

typescript
// 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

typescript
@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)

javascript
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

  1. Explicit entryPoint in config (highest)
  2. package.json exports['.'].import or exports['.']
  3. package.json main field
  4. Fallback: ./dist/index.js

Architecture

  • Lazy command loading -- each command is loaded on demand (~100ms first-use overhead)
  • Manifest discovery -- auto-finds .smrt/manifest.json and scans node_modules/@happyvertical/smrt-*
  • Class loading order -- config.entryPointpackage.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 loading
  • src/commands/ -- individual command implementations
  • src/loaders/ -- class-loader, local-loader, npm-loader, git-loader, template-loader
  • src/discovery/manifest-discovery.ts -- manifest auto-discovery
  • src/commands/docs-claude.ts -- downstream CLAUDE.md generation (~550 lines)

Gotchas

  • Test mode detection: checks NODE_ENV=test, VITEST=true, and globals like it / 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:history should distinguish active live drift from superseded failed generated schema repairs. Treating every failed row as a current blocker is a known follow-up.

Related Modules