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-cliOr use the installation script:
./install-cli.shVerify installation:
ultimo --version
# ultimo 0.4.1Commands
Generate Client
Generate a TypeScript client from your Rust backend:
ultimo generate --project ./backend --output ./frontend/src/lib/client.tsShort form:
ultimo generate -p ./backend -o ./frontend/src/client.tsThis 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
waitWatch 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
donePre-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
fiCI/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 testConfiguration 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 = trueThen use:
ultimo generate # Uses config file settingsCreate a new project
Scaffold a new Ultimo project:
ultimo new my-app --template fullstackAvailable 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 interfacesThe 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.
| Command | Status | Use instead |
|---|---|---|
ultimo build | Planned — production build wrapper | cargo build --release |
ultimo test | Not planned yet | cargo test |
ultimo fmt / ultimo lint | Not planned yet | cargo 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 --helpBest 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.tsOutdated Client
# Regenerate client
ultimo generate -p ./backend -o ./frontend/src/lib/client.ts
# Clear frontend build cache
cd frontend && rm -rf node_modules/.viteCLI Not Found
# Reinstall CLI
cargo install --path ultimo-cli --force
# Add to PATH
export PATH="$HOME/.cargo/bin:$PATH"
# Verify
which ultimo
ultimo --version