Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

CLI Tools

The Ultimo CLI provides commands for generating TypeScript clients, managing projects, and development workflows.

Installation

Install the CLI from your Ultimo project:

cargo install --path ultimo-cli

Or use the installation script:

./install-cli.sh

Verify installation:

ultimo --version
# ultimo 0.4.1

Commands

Generate Client

Generate a TypeScript client from your Rust backend:

ultimo generate --project ./backend --output ./frontend/src/lib/client.ts

Short form:

ultimo generate -p ./backend -o ./frontend/src/client.ts

This runs a generate-client binary in your project (cargo run --bin generate-client -- <output>), so the client is produced by your real RPC registry — no source parsing, no guessing.

Add src/bin/generate-client.rs to your project:

// src/bin/generate-client.rs
fn main() {
    let out = std::env::args().nth(1).expect("usage: generate-client <output>");
    let rpc = my_app::build_registry(); // build the same RpcRegistry your server uses
    rpc.generate_client_file(&out).expect("failed to write client");
}

The binary receives the --output path as its first argument and writes the client there (see TypeScript Clients for building the registry). Derive your RPC types with #[derive(ultimo::rpc::TS)] and enable the client-gen feature so the client carries real types.

Example Output

// Auto-generated by Ultimo CLI
// DO NOT EDIT MANUALLY
 
export interface User {
  id: number;
  name: string;
  email: string;
}
 
export interface GetUserInput {
  id: number;
}
 
export class UltimoRpcClient {
  private baseUrl: string;
 
  constructor(baseUrl: string = "/rpc") {
    this.baseUrl = baseUrl;
  }
 
  async getUser(params: GetUserInput): Promise<User> {
    return this.call("getUser", params);
  }
 
  // ... more methods
}

Workflow Integration

Development Script

Add client generation to your development workflow:

#!/bin/bash
# dev.sh
 
echo "🔄 Generating TypeScript client..."
ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
 
echo "🚀 Starting backend..."
cd backend && cargo run --release &
BACKEND_PID=$!
 
echo "🎨 Starting frontend..."
cd frontend && npm run dev &
FRONTEND_PID=$!
 
# Cleanup on exit
trap "kill $BACKEND_PID $FRONTEND_PID" EXIT
 
wait

Watch Mode

Regenerate client on backend changes:

#!/bin/bash
# watch-generate.sh
 
echo "👁️  Watching for changes..."
 
while true; do
  inotifywait -r -e modify ./backend/src
  echo "🔄 Regenerating client..."
  ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
done

Pre-commit Hook

Ensure client is up-to-date before commits:

# .git/hooks/pre-commit
#!/bin/bash
 
echo "🔄 Regenerating TypeScript client..."
ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
 
if [ $? -eq 0 ]; then
  git add frontend/src/lib/client.ts
  echo "✅ Client updated and staged"
else
  echo "❌ Client generation failed"
  exit 1
fi

CI/CD Pipeline

# .github/workflows/ci.yml
name: CI
 
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
 
      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
 
      - name: Install Ultimo CLI
        run: cargo install --path ultimo-cli
 
      - name: Generate TypeScript client
        run: ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
 
      - name: Check for changes
        run: |
          if ! git diff --quiet; then
            echo "❌ TypeScript client is out of date"
            exit 1
          fi
 
      - name: Run tests
        run: cargo test

Configuration File

Create ultimo.toml for project-specific settings:

[project]
name = "my-app"
version = "1.0.0"
 
[generate]
backend_path = "./backend"
output_path = "./frontend/src/lib/client.ts"
base_url = "/api/rpc"
 
[dev]
backend_port = 3000
frontend_port = 5173
hot_reload = true

Then use:

ultimo generate  # Uses config file settings

Create a new project

Scaffold a new Ultimo project:

ultimo new my-app --template fullstack

Available templates: basic, fullstack, api-only, rpc, production.

Development Server

Start a hot-reload development server that watches for file changes and automatically recompiles and restarts:

ultimo dev                       # default: 127.0.0.1:3000
ultimo dev --port 8080           # custom port
ultimo dev --host 0.0.0.0        # bind to all interfaces

The dev server watches src/**/*.rs and Cargo.toml. On any change it kills the running server, rebuilds with cargo build, and restarts. Compilation errors are printed inline — the watcher stays active so the server restarts automatically once you fix the error.

Future Commands

⚠️ Not implemented yet. These commands exist as placeholders and currently print a notice. Until they land, use the cargo equivalents.

CommandStatusUse instead
ultimo buildPlanned — production build wrappercargo build --release
ultimo testNot planned yetcargo test
ultimo fmt / ultimo lintNot planned yetcargo fmt / cargo clippy

CLI Help

View help for any command:

# General help
ultimo --help
 
# Command-specific help
ultimo generate --help
ultimo dev --help
ultimo build --help

Best Practices

✅ Automate Generation

Add to your build script:

{
  "scripts": {
    "prebuild": "ultimo generate",
    "build": "vite build",
    "dev": "ultimo generate && vite"
  }
}

✅ Version Control

Commit the generated client to track changes:

# .gitignore

# Don't ignore generated client
!frontend/src/lib/client.ts

✅ Verify in CI

Ensure the client is always up-to-date:

- name: Verify client is up-to-date
  run: |
    ultimo generate
    git diff --exit-code frontend/src/lib/client.ts

✅ Document Commands

Add to your README:

## Development
 
Generate TypeScript client:
\`\`\`bash
ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
\`\`\`
 
Start development servers:
\`\`\`bash
./dev.sh
\`\`\`

Troubleshooting

Client Generation Fails

# Ensure backend compiles
cd backend && cargo check
 
# Verify project structure
ultimo generate --verbose
 
# Check output path is writable
touch ./frontend/src/lib/client.ts

Outdated Client

# Regenerate client
ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
 
# Clear frontend build cache
cd frontend && rm -rf node_modules/.vite

CLI Not Found

# Reinstall CLI
cargo install --path ultimo-cli --force
 
# Add to PATH
export PATH="$HOME/.cargo/bin:$PATH"
 
# Verify
which ultimo
ultimo --version