Leadmetrics CLI (lm)
App:
apps/cli· Audience: Internal Leadmetrics / DM team · Status: [To Build]
lm is an interactive REPL for the Leadmetrics internal team — a terminal interface for managing tenants, running agents, reviewing and approving content, monitoring queues, and interacting with the platform without opening a browser. Modelled on the Claude Code / Gemini CLI experience: natural language + slash commands, streaming agent output, rich terminal UI.
Design Principles
| Principle | What it means |
|---|---|
| Interactive first | Every session is a REPL, not a one-shot command runner. Context persists between commands. |
| Natural language + slash commands | You can type commands (/approve 1234) or describe intent in plain English (“approve all low-risk blog posts for Acme today”) |
| Streaming output | Agent runs stream token-by-token to the terminal, just like watching Claude Code work |
| Tenant context | One active tenant at a time. Commands apply to it automatically. Switch with /use <tenant> |
| API-backed | All operations go through the same Fastify API the DM Portal uses (/dm/v1, /admin/v1) — no direct DB access |
| Offline-aware | Graceful degradation when the API is unreachable; reconnect with /reconnect |
Tech Stack
| Layer | Choice | Why |
|---|---|---|
| Runtime | Node.js 22 + TypeScript | Same runtime as the rest of the monorepo |
| REPL / input | readline + custom prompt loop | Full control over input history, multiline, keybindings |
| UI rendering | Ink (React for CLIs) | Tables, spinners, progress bars, streaming text — declarative |
| Streaming | Native EventSource / fetch with streaming body | SSE from /dm/v1/activities/:id/stream |
| Auth | API key stored in ~/.lm/config.json (encrypted at rest) | No OAuth flow required for internal tools |
| HTTP client | ofetch | Same client used in the monorepo |
| Syntax highlighting | chalk + cli-highlight | Colour-codes Markdown, JSON, code in output |
| Config | ~/.lm/config.json | Per-user, not per-repo |
Monorepo placement
apps/
cli/
src/
index.ts # Entry point — starts the REPL
repl.ts # Main REPL loop
context.ts # Session context (current tenant, auth, history)
commands/ # One file per slash command
nl/ # Natural language query handler
ui/ # Ink components (tables, spinners, stream view)
api/ # Typed API client (wraps /dm/v1 and /admin/v1)
config.ts # ~/.lm/config.json read/write
package.json
tsconfig.jsonInstallation
# From the monorepo root
pnpm --filter cli build
pnpm --filter cli link # makes `lm` available globally
# Or install from the private npm registry
npm install -g @leadmetrics/cli
# Verify
lm --versionFirst-Time Setup
$ lm
Welcome to Leadmetrics CLI v1.0.0
No config found. Running first-time setup.
API URL › https://api.leadmetrics.io
API Key › lm_live_••••••••••••••••••••••
Authenticating... ✅ Logged in as moble@leadmetrics.io (super_admin)
Config saved to ~/.lm/config.json
Type /help to see available commands, or just describe what you want to do.
lm ›The REPL
lm ›The prompt shows your current tenant context when one is active:
lm [Acme Corp] ›Input modes
| Mode | How to activate | Example |
|---|---|---|
| Slash command | Start with / | /queue, /approve 1234 |
| Natural language | Just type | show pending approvals for Acme |
| Multiline | End line with \ | approve 1234 \ ↵ reason: looks good |
| Pipe / stdin | echo "..." | lm | echo "/status" | lm --no-interactive |
Keyboard shortcuts
| Key | Action |
|---|---|
↑ / ↓ | Navigate command history |
Ctrl+C | Cancel current command / interrupt streaming |
Ctrl+D | Exit |
Ctrl+L | Clear screen |
Tab | Autocomplete slash commands and tenant names |
Ctrl+R | Reverse search command history |
Session Context
The CLI maintains a session context for the duration of your REPL session:
interface SessionContext {
user: AuthUser; // who is logged in
currentTenant: Tenant | null; // active tenant (/use sets this)
history: CommandEntry[]; // command history (persisted to ~/.lm/history)
streaming: boolean; // whether a stream is currently active
}When currentTenant is set, all tenant-scoped commands (queue, approve, agents, cost, etc.) default to it without requiring --tenant.
Auth
# Login / set API key
lm auth login
# Show current auth
lm auth whoami
# Logout / clear config
lm auth logoutAPI keys are stored in ~/.lm/config.json encrypted with the machine’s unique hardware ID (same pattern as VS Code credential storage). The config file is never committed to source control.
{
"apiUrl": "https://api.leadmetrics.io",
"apiKey": "<encrypted>",
"defaultTenant": null,
"theme": "dark"
}Non-Interactive Mode
For scripting and CI use:
# Run a single command and exit
lm --run "/status"
# Pipe commands
echo "/queue --status pending" | lm --no-interactive
# Output as JSON (for scripting)
lm --run "/tenants" --output json
# Quiet mode (no colour, no spinners)
lm --run "/approve 1234" --quietConfiguration
# Set default tenant (skips /use on every session)
lm config set defaultTenant acme-corp
# Switch API environment
lm config set apiUrl https://staging.api.leadmetrics.io
# Set colour theme
lm config set theme dark|light|none
# Show current config
lm config showRelated Docs
- Commands Reference — every slash command, flags, and output
- Workflows — common team tasks step by step
- DM Portal — the browser equivalent
- API — DM — the API surface the CLI calls
- API — Admin — admin-only commands